How to select file on fs with react and electron? - javascript

I'm building desktop application with Electron and ReactJS.
I need to implement feature to select file from file system, like input="file" works in forms.
Actually, all I need is to obtain absolute path of the file.
How can I achieve that?
I tried:
<input type="file" onChange={function(e) {console.log(e.target.value)} } />
But it returns fakepath due to security reasons.
I think Dialogs in Electron may be useful for this purpose, but how to propagate file path to react application then?

const {dialog} = require('electron').remote;
...
document.querySelector('#fileSelect').addEventListener('click', function (event) {
dialog.showOpenDialog({
properties: ['openFile', 'multiSelections']
}, function (files) {
if (files !== undefined) {
// handle files
}
})
});

Related

Webpack, build one JSON file from multiple JSON files at build time

I have to build a landing page which when visited will check your User Agent and determine which device you are using and if you are using a device that is supported it will show some information for that device. The information that is shown does not matter that's done, checking User Agent is also done.
What is not done is this because I am stuck. I have directory structure like this:
-devices
-nintendo
config.json
-xbox
config.json
-ps5
config.json
and so on. So since I need to access these configs at runtime since I will not know which device the user is using until they land on the page, this cannot be determined at build time. So my thoughts are have a JS file that will loop through all of these folders and take each config and build one big config that will be a module in web pack that I can import in my main.js file so I can than just import it and access the properties using dot notation.
Can I do all of this with web pack and skip the JS file that will combine all of the config, or is there a better approach to this?
You don't really need anything extra to achieve this. Assuming you have following directory structure:
- main.js
- devices
- nintendo
- config.json
- xbox
- config.json
- ps5
- config.json
All you have to do is to import individual JSONs in your main.js file:
import nintendo from './devices/nintendo/config.json';
import xbox from './devices/xbox/config.json';
import ps5 from './devices/ps5/config.json';
function main() {
// Read `someDevice` at runtime
if (someDevice === 'nintendo') {
console.log(nintendo);
}
}
If your json files are big and bundle size becomes a concern, then you can use dynamic imports along with async-await and Webpack will automatically split the bundle for you:
async function main() {
if (someDevice === 'nintendo') {
const nintendo = await import('./devices/nintendo/config.json');
} else if (someDevice === 'xbox') {
const xbox = await import('./devices/nintendo/config.json');
} else {
const ps5 = await import('./devices/ps5/config.json');
}
}

Send ipcRenderer variable to vue3 page

I have a vue3 app with electron and need to send a variable from the ipcRenderer to my Vue3 page. I cant figure out how to do it especially given vue strips away lots of js. What im trying to do is to save the path of a folder which so far works fine, then display it in the vue3 app either in a span or whatever. I successfully got the value i need to display to the ipcRenderer but cant access it using my vue app.
Vue3 page
<q-btn
id="showpath"
dark
flat
size="xl"
label="show Path"
type="submit"
#click="showpath"
/>
</div>
export default {
name: "Settings",
props: {},
methods: {
loader() {
window.postMessage({
type: "select-dirs",
});
},
showpath() {
const testa = window.postMessage({ type: "pathtf"})
console.log("Vue page says :"+ testa)
},
},
};
</script>
All I get here is "undefined
Preloader script
const { ipcRenderer } = require('electron');
const settings = require('./settings');
process.once('loaded', () => {
window.addEventListener('message', evt => {
if (evt.data.type === 'select-dirs') {
ipcRenderer.send('select-dirs')
}
}),
window.addEventListener('message', evt => {
if (evt.data.type === 'pathtf') {
const pathtf = settings.gettfpath("pathtf")
console.log(pathtf)
}
})
})
The console.log in the preload file works and displays the value, but i cant get that value to my vue3 page.
Any tip? Thank you
I would suggest that you use electron's contextBridge to expose certain methods (send/receive) to the renderer process.
I do not know what your gettfPath method does but if that variable is available to you in the preloadjs file you should be able to expose it as a variable like so:
const {contextBridge} = require("electron");
contextBridge.exposeInMainWorld("electronApi", {
pathtf: settings.gettfPath()
});
With this your path will be exposed to your renderer process as window.electronApi.pathtf
Here are two resources you may find helpful:
How to use preload.js properly in Electron
https://medium.com/swlh/how-to-safely-set-up-an-electron-app-with-vue-and-webpack-556fb491b83 (This one may not be exactly what you are looking for but it has a good explanation / example of how to use ipc with view and electron)

Can't resolve 'fs' in \node_modules\electron

I am using the electron-react boilerplate and want to use an electron dialog in App.tsx:
const { dialog } = require('#electron/remote') //also tried with import
const Hello = () => {
const readFromFile = async () => {
dialog.showOpenDialog({})
}
return (
<>
<button onClick={() => readFromFile()} >Test</button>
</>
)
}
in main.ts I placed the following line at the top
require('#electron/remote/main').initialize()
In the end I always get this error:
Module not found: Error: Can't resolve 'fs' in 'C:\Users\myUsername\source\repos\electronTest\node_modules\electron'
I also tried nodeIntegration: true and contextIsolation: false
Just spent a while on this and <rant about JS community>... eh. Maybe this will help others.
tl;dr
You can only use electron's imports in electron-main side.
longer story
Electron is split into electron-main and electron-renderer. As others suggested, you need to update webpack's config with the target pointing at the correct electron.
In case you're using one of the electron builder boilerplates, you might see directory .erb with a few webpack configs. Especially, there might be one with target = "electron-main" and another with target = ["web", "electron-renderer"]. So it feels like mission accomplished; however, according to webpack's doc on target, if you pass a list, then a common config is set. Since web doesn't include fs, the common part also won't include fs.
For the reason above, some of electron imports, e.g. clipboard, can be only used from the "electron-main" side of your application.
The work-around, e.g. using clipboard on the application side, is to use IPC to communicate between main and renderer sides.
Check your webpack.config.js. Looks like you target is not electron-main or electron-renderer.

Equivalent to directory, mozdirectory and webkitdirectory in react?

I want to upload directories to my server using react and I tried using directory, mozdirectory, and webKitDirectory attributes but they didn't work and I tried to run the code that is below but unfortunately, it didn't work.
function UploadDirectory() {
function dirUploadAttr(input) {
input.setAttribute("webkit-directory", "");
input.setAttribute("moz-directory", "");
}
return (
<input type="file" ref={dirUploadAttr} />
)
}
How do I take directories in the input tag in react?
What is the effective and simple way to do it?
I'll be honest, me and a team member at the corporation I've worked at have looked into this issue for several weeks. The issue being that the users we were making our app for wanted a way to strip a file from a directory and upload it without having to double click in the directory itself to obtain the file (Ex. Click the folder containing the file in the upload menu and selecting upload to upload whole folder). Well, after digging and digging we got into contact with multiple teams in order to find a solution and they told us this: "webkitdirectory (or anything like it for that matter) is not supported in React and that's due to a Windows limitation." I believe it could have been done in the .NET era but React's specific <input)> html tag doesn't support webkitdirectory as an attribute. We instead incorporated a dropzone from the node module react-dropzone-uploader:
import Dropzone from "react-dropzone-uploader";
import { getDroppedOrSelectedFiles } from './Html5FileSelector'
const getFilesFromEvent = (e:any) => {
return new Promise<any>(resolve => {
getDroppedOrSelectedFiles(e).then(chosenFiles => {
resolve(chosenFiles.map((f:any) => f.fileObject))
})
})
};
const handleSubmit = (files:any) => { this.fileChange(files); }
<Dropzone accept=".txt" getFilesFromEvent={getFilesFromEvent} onSubmit=
{handleSubmit} />
Note that you will have to implement a function from Html5FileSelector, more information can be obtain from the live example showcasing the dropzone uploader, this is also a link to the official documentation of the node module:
https://react-dropzone-uploader.js.org/docs/examples#!/Custom%20Input,%20Directory%20Drag%20and%20Drop

How to get folder path using electron

I am very new to the electron. Can anyone suggest me how to get a local folder's relative path using the electron? JavaScript does not have that capability.
I have a Choose File button(see snapshot), so my question is that when I select a folder and click on the open button then it should return a whole directory path.
As #phuongle pointed out in the comments you want to use showOpenDialog(). Something like this:
var remote = require('remote');
var dialog = remote.require('electron').dialog;
var path = dialog.showOpenDialog({
properties: ['openDirectory']
});
UPDATE: If the above isn't working for your current Electron version, you should try more modern importing:
const {dialog} = require('electron').remote;
In addition, in order to use remote, you need to set enableRemoteModule when creating your window in your main process:
const myWindow = new BrowserWindow({
webPreferences: {
enableRemoteModule: true
}
});
Following the official IPC tutorial worked for me
main process:
import {dialog, ipcMain} from 'electron'
function createWindow () {
mainWindow = new BrowserWindow({/*Your electron window boilerplate*/})
ipcMain.handle('dialog:openDirectory', async () => {
const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, {
properties: ['openDirectory']
})
if (canceled) {
return
} else {
return filePaths[0]
}
})
}
preload script:
import {contextBridge, ipcRenderer} from 'electron'
contextBridge.exposeInMainWorld('myAPI', {
selectFolder: () => ipcRenderer.invoke('dialog:openDirectory')
})
Now you can call the selectFolder method from your application code and get the user input.
window.myAPI.selectFolder().then(result=>{/* Do something with the folder path*/})
In Electron we can select the directory by specifying simple input element with type="file" and webkitdirectory attribute'.
<input id="myFile" type="file" webkitdirectory /> and we can get the directory full path with the path property of File object document.getElementById("myFile").files[0].path
You would use Node's path.relative for that.
The solution for me was simply using all in lowercase, with a value true as string in my react component. No extra configuration was required.
Like so:
<input
id="path-picker"
type="file"
webkitdirectory="true"
/>
Edit
It turns out that, as mentioned by #cbartondock, it will recursively look for files in the directory, which is not good!
I ended up using the required electron remote's dialog.

Categories

Resources