初识electron
electron项目的结构如下:

其中package.json是用来描述一些配置信息以及一些快速启动的指令等信息的
main.js则是用来整个项目的主线程,用于创建窗口和处理系统事件。
index.html就是布局啦。
运行
package.json文件里有这样一句描述:

这就是快速启动指令,只需要npm start就可以启动你的electron应用了。
创建渲染器进程
对于渲染器进程,大家给的理解是主线程是无法显示的,需要通过BrowserWindow来创建新的窗口,每一个窗口维护一个渲染器进程。
我的文件目录如下:

app目录下的js/index.js就可以看作一个渲染器进程。
主进程和渲染器进程的区别

界面
界面可以通过html+css实现
我的界面如下:

选择文件存放文件夹
此处需要用到electron的一个模块dialog,但是由于其为主线程内可用的模块,所以在渲染器进程使用的时候必须加上.remote。
remote 模块提供了一种在渲染进程(网页)和主进程之间进行进程间通讯(IPC)的简便途径。
所以在渲染器进程处加上以下语句获取dialog模块
const {dialog} = require('electron').remote
选择文件夹具体语句如下
button_choose.addEventListener("click", function(){ dialog.showOpenDialog({ //默认路径 defaultPath :'../Desktop', //选择操作,此处是打开文件夹 properties: [ 'openDirectory', ], //过滤条件 filters: [ { name: 'All', extensions: ['*'] }, ] },function(res){ //回调函数内容,此处是将路径内容显示在input框内 downloadFolder.value = res[0]; }) });
实现下载文件
这就是我最爆炸的部分。完成这个应用的一半时间都贡献给了这部分。
先进行简单的校验
button_download.addEventListener("click", function(){ var tips = document.getElementsByClassName("tips")[0]; if(downloadFolder.value!="" &&downloadAddress.value!="") { //下载文件 } else if(downloadAddress.value=="") { tips.innerText = "未填写下载地址"; } else { tips.innerText = "未选择文件夹" } })
下载文件我使用的是will-download,寻找一个合适的模块实现真的很心累,这里就不赘述了。
由于will-download是主进程的模块调用的,所以此处就需要主进程和渲染进程的通信。
ipcMain和ipcRenderer
通信原理如下:
// 后台进程 const {ipcMain} = require('electron') ipcMain.on('create', (event, person) => { console.log('creating', person) // 输出:"creating harttle" event.sender.send('born', person) }); // 渲染进程 const {ipcRenderer} = require('electron') ipcRenderer.on('born', (event, person) => { console.log(person, 'born') // 输出 "harttle born" }); ipcRenderer.send('create', 'harttle')
所以我的想法是,在主进程里监听一个download事件,在渲染进程里当点击下载时调用这个事件。
//主进程代码 ipcMain.on('download', (evt, args) => { var arr = args.split("+"); downloadpath = arr[0]; folderpath = arr[1]; evt.sender.send('tips',downloadpath); mainWindow.webContents.downloadURL(downloadpath); }); //渲染器进程代码 ipcRenderer.send('download',downloadAddress.value+"+"+downloadFolder.value);
下面这句会触发will-download事件
mainWindow.webContents.downloadURL(downloadpath);
下面来设置监听will-download事件的回调函数
mainWindow.webContents.session.on('will-download', (event, item, webContents) => { //设置文件存放位置 item.setSavePath(folderpath+`\\${item.getFilename()}`); item.on('updated', (event, state) => { if (state === 'interrupted') { console.log('Download is interrupted but can be resumed') } else if (state === 'progressing') { if (item.isPaused()) { console.log('Download is paused') } else { console.log(`Received bytes: ${item.getReceivedBytes()}`) } } }) item.once('done', (event, state) => { if (state === 'completed') { console.log('Download successfully') } else { console.log(`Download failed: ${state}`) } }) })
ipc到ipcMain和ipcRender
其实我一开始使用的是ipc进行两个进程之间的通信,但是不论我怎么定义,ipc总是报错,报其没有send和on方法。
又疯狂找资料,遇到这个问题的人还真不多,所以没有找到解决方法。
但是后来发现,与主进程的通信可以使用ipcMain,所以就弃用ipc了。
找不到为什么错真的很伤自尊啊,希望下次可以发现。
主进程更改后刷新页面没有改变
这个的解决方法是退出应用然后重新npm start一次。
这个很好理解吧,重新刷新页面只是更新了渲染器进程而非主进程。
item.setSavePath()问题
这个方法接受一个参数即文件存放路径。
但是!一定保证路径里包含了文件名!一定!
否则,就算路径不正确它也不会报错而是选择存放在默认路径下。
ipcMain和ipcRender的传参问题
ipcMain/ipcRender.send(eventname, arg);
此处的arg是一个参数而不是参数数组。
所以由于要传下载地址和文件存放地址,我选择使用“+”把它们连接起来。