Recently, in the development process, I met a strange and familiar demand: selecting folders. It seems simple, but in fact there are pits. This paper records my pit mining process.
Requirement description
The user selects a folder (note that it is not a file, but a folder), and then gets the local global path of the folder from the front end for a series of other operations.
The first idea
The first thought of front-end development is to use Input and then Type=file to obtain the file path, so that you can get the path. Therefore, I implemented a version:
function () { let inputObj = document.createElement('input'); inputObj.setAttribute('id', '_ef'); inputObj.setAttribute('type', 'file'); inputObj.setAttribute('style', 'visibility:hidden'); inputObj.setAttribute('webkitdirectory', ''); inputObj.setAttribute('directory', ''); document.body.appendChild(inputObj); inputObj.addEventListener('change', function (a,b,c) { let obj = document.getElementById('_ef'); let files = obj.files; try{ if (files.length > 1 || files.length === 0) { console.log('please choose a file'); } else { console.log(files); } obj.removeEventListener('change', function(){ console.log('remove change event listener success'); }); document.body.removeChild(obj); } catch (e){ console.error(e); } }); inputObj.click(); }
Create a new hidden Input, listen to the Change event, and get the path of the folder according to the path of the internal file. However, the final result is as follows:
Yes, you get not only a relative path, but also a system prompt of the browser, as shown in the figure below, which is very unfriendly. Usually we can choose the directory when we develop and use vscade. How do we do it?
Preliminary study on Terminal Scheme
After a search, you find that the front-end does not have this ability, and the browser will not let you operate like this. Because the test tool uses Electron, it turns its attention to the terminal. Soon, it was found that someone had implemented it.
According to the article: electron opens the select file box This function is simply realized:
But when I ran the code locally, I was pleasantly surprised to find that the error was reported. It was poisonous:
It is said that FS is referenced in the code in Electron, and then an error is reported because FS cannot be found. After searching, it is found that it is Webpack 5 because some nodes are no longer built in the new version of Webpack JS, so when you have dependencies, you need to manually import them through fallback:
However, I found that there was no fs module in the core module. How angry~
Try one
So I began to try to introduce Electron through Externals, which is like this:
No, it will report require is not defined, that is, Externals will try to write a reference to the compiled file:
const electron = require('electron');
Although it can be written, it cannot be parsed. Unable to achieve the effect of introduction,,,
Try two
Now how to solve the problem of introducing the Electron module, so I turned to the Electron Api and found that there is a preloaded Api that can execute some Js logic after the program Dom is loaded,
I'm very happy. As long as I mount the module under the Window, I can make the front end call, so the implementation is as follows:
main.js
let mainWindow = new BrowserWindow({ width: width, height: height, minHeight: 338, minWidth: 600, show: false, autoHideMenuBar: true, webPreferences: { preload: path.join(__dirname, 'preload.js') } });
preload.js
window.ipcRenderer = require('electron').ipcRenderer;
After running, I still can't read!!!!! There is no ipcRenderer attribute under the window, but it has been set in preload, so locate preload JS sure enough:
The printed object is actually a global object. Why? Isn't the window here the same as the front-end window? I can't figure out what's wrong with the implementation.
Try three
The front-end window does not have this attribute, which indicates that it has not been set successfully, or they are not variables in the same scope. After checking the API again, we find the clue:
In the default configuration of webPreferences, contextIsolation is True by default, that is, the context in which the default preloaded script runs can only access its own special documents and global windows. So, is this the reason why setting the Window property fails? So try to turn it off:
main.js
let mainWindow = new BrowserWindow({ width: width, height: height, minHeight: 338, minWidth: 600, show: false, autoHideMenuBar: true, webPreferences: { contextIsolation: false, preload: path.join(__dirname, 'preload.js') } });
success! Finally, after many attempts, the terminal API was successfully obtained, and the folder selection function was implemented gracefully.
summary
Mixed development still needs to adjust the thinking. The front-end thinking is easy to hit a wall. There is also the need to carefully read the API, especially for applications that are not very familiar at ordinary times. Encourage each other.