因为这次项目中涉及对文件的大量操作,其中就包括了对操作文件的输出目录的要求,所以本文记录一下将文件保存到指定路径的一些解决方案。这其中有些方法是借助electron完成的,有些则直接通过node对文件读写完成的。
话不多说,开始逐个介绍下这次项目中用到的一些方法。
方法一(通过electron本身api实现)
这个方法主要借助electron提供的api来实现的。先上代码:
/**
* description: 下载文件到指定目录
* param {string} url 文件下载链接
* param {string} fileName 文件名称
* param {string} fileType 文件格式
* author: longyunfei
*/
function downloadFileToFolder(url, fileName, fileType) {
win.webContents.downloadURL(url);
win.webContents.session.once('will-download', (event, item, webContents) => {
//设置保存路径
const filePath = path.join(app.getAppPath(), '/download', `${fileName}.${fileType}`);
item.setSavePath(filePath);
// item.on('updated', (event, state) => {
// if (state === 'interrupted') {
// console.log('下载中断,可以继续');
// } else if (state === 'progressing') {
// if (item.isPaused()) {
// console.log('下载暂停');
// } else {
// console.log(`当前下载项目的接收字节${item.getReceivedBytes()}`);
// console.log(`下载完成百分比:${item.getReceivedBytes() / item.getTotalBytes() * 100}`);
// }
// }
// });
item.once('done', (event, state) => {
if (state === 'completed') {
shell.openPath(filePath) //打开文件
}
})
})
}
这个方法里面涉及到了很多electron的api,容我慢慢道来。
首先win
变量是我声明的窗口(BrowserWindow)对象,具体移步BrowserWindow。
webContents是窗口对象身上的一个属性,它主要负责渲染和控制网页。
调用webContents
身上的downloadURL
方法下载文件,到这我们完成了文件下载功能,其实在网页里执行下window.open
就达到了这个效果,接下来就是将下载好的文件存取到指定目录。
当我们通过downloadURL
方法进行资源下载的时候,会触发session
身上的will-download
事件,所以我们监听这个事件来完成我们的目的。
will-download事件的三个参数
- event 事件参数
- item 下载的项目实例
- webContents 网页内容
这里我们主要关注item参数,它身上又是一堆事件和方法,我们用到setSavePath
来设置下载项的保存文件路径(我这里将下载地址放在了程序根目录下的download文件夹下),同时监听done
事件在下载完成时执行一些操作,比如这里的打开文件。
同时监听updated
事件我们可以得到下载过程中的具体情况比如下载完成的百分比,同时搭配item
身上暂停下载的pause
方法,恢复下载的resume
方法,也可以实现一些特定的业务操作,比如断点下载。
整体看下来一套api组合实现了这个下载方法,不过对着文档找api的过程也挺耗时的,特别是还不熟悉的情况下。
这里还遗留了个小问题,这个方法接收的是下载地址。在项目中后端同学这里给到我的是个文件流,直接执行方法并没有下载成功,所以在渲染进程中,要先将流转换成下载链接再通过通信将下载链接传给主进程执行上面的方法。
//将流转换为下载地址
function getDownLoadUrl(data) {
const blob = new Blob([data]);
return window.URL.createObjectURL(blob);
}
方法二(通过node实现)
这个方法通过node来实现,同样先上代码
const request = require('request');
const path = require('path');
const fs = require('fs');
/**
* description: 下载文件到指定目录
* param {string} fileName 文件名称
* param {string} fileType 文件格式
* author: longyunfei
*/
function downloadFileToFolder( fileName, fileType) {
//设置保存路径
const targetFolder = path.join(__dirname, '/download');
//创建可写流
let stream = fs.createWriteStream(path.join(targetFolder, `${fileName}.${fileType}`));
request({
url: `接口地址`,
method: 'GET'
})
.pipe(stream)
.on('close', (err, response, body) => {
stream.close();
if(err) return
shell.openPath(path.join(targetFolder, `${fileName}.${fileType}`)) //打开文件
});
}
node就简单多了呀,根据传入的文件名和文件类型创建了一个可写流,然后通过接口获取文件流再通过pipe方法写入,最后在通过close
事件关闭可写流并打开文件。
方法三(通过node实现)
依旧基于node实现。
const request = require('request');
const path = require('path');
const fs = require('fs');
/**
* description: 下载文件到指定目录
* param {string} fileName 文件名称
* param {string} fileType 文件格式
* author: longyunfei
*/
function downloadFileToFolder( fileName, fileType) {
//设置保存路径
const targetFolder = path.join(__dirname, '/download');
let stream = fs.createWriteStream();
request({
url: `接口地址`,
method: 'GET'
}, function (err, response, body) {
fs.writeFile(path.join(targetFolder, `${fileName}.${fileType}`), body , function (err) {
if (err) {
console.log(err);
} else {
console.log('ok.');
shell.openPath(path.join(targetFolder, `${fileName}.${fileType}`)) //打开文件
}
})
}
这里调取接口数据后,直接执行fs.writeFile
方法写入文件,此处需要注意一下传入参数:
- filename(String): 文件名称,前面可拼接上具体路径达到下载到指定目录的目的
- data(String | Buffer): 需要写入的内容,但只能是
字符串
或者buffer
数据。 -
- option(Object):配置项
- encoding(String): 可选值,默认
utf8
,当data使buffer
时,该值应该为 ignored。 - mode(Number):文件的读写权限,默认值438
- flag(String):默认值'w'
- callback(Function):回调函数,传递一个异常参数err
使用场景
最后,附上此次在项目中使用到的部分业务场景。
场景一(截屏转换文件格式)
这里实现的功能是,快捷键截屏将截屏图片转化为word保存到本地并打开文件,此处使用了方法二来实现。
场景二(拖拽转换文件格式)
上篇文章说到的悬浮球中拖拽文件转换相应格式的功能,则用到的上述的方法一借助渲染进程和主进程的通信来实现。
总结
这里归纳了三种方法来将文件保存到指定目录,如果你更多的实现方法,欢迎再评论区一起讨论哈。