公司需要搞electron项目,我在这方面又是没接触过,上网搜也是没找到完整的代码(也可能我找的关键词不够精确),最后只能自己摸石头过河。记录下遇到的问题和解决的方法
在 Electron-Vue 项目中,下载文件到指定目录,需要在 Electron 的主进程中处理下载逻辑,并使用 Node.js 的 fs
模块和 axios
或其他 HTTP 客户端库来下载和保存文件。
环境:
"axios": "^1.6.8",
"path-browserify": "^1.0.1",
"qrcode": "^1.5.3",
"vue": "^2.6.14",
"vue-router": "^3.5.1"
初始示例代码
1. 主进程 (main.js
或其他主进程文件,我主进程文件名background.js)
在主进程中,你需要监听来自渲染进程的 IPC 消息,处理文件下载到指定目录。
const { ipcMain } = require('electron');
const fs = require('fs');
const axios = require('axios');
const path = require('path');
// 假设这是你的指定目录
const specifiedDirectory = path.join(__dirname, 'downloads');
// 确保目录存在
if (!fs.existsSync(specifiedDirectory)) {
fs.mkdirSync(specifiedDirectory, { recursive: true });
}
ipcMain.on('download-file', async (event, options) => {
try {
// options 应该包含 url 和文件名
const { url, filename } = options;
const filePath = path.join(specifiedDirectory, filename);
// 使用 axios 下载文件
const response = await axios({
method: 'get',
url: url,
responseType: 'stream'
});
// 写入文件
const writer = fs.createWriteStream(filePath);
return new Promise((resolve, reject) => {
response.data.pipe(writer);
let error = null;
writer.on('error', err => {
error = err;
writer.close();
reject(err);
});
writer.on('close', () => {
if (!error) {
resolve(true);
event.reply('download-file-reply', { success: true, filePath });
} else {
event.reply('download-file-reply', { success: false, error });
}
});
});
} catch (error) {
console.error('下载文件时出错:', error);
event.reply('download-file-reply', { success: false, error });
}
});
2. 渲染进程 (Vue 组件)
在 Vue 组件中,你可以通过 Electron 的 IPC 机制发送消息到主进程,请求下载文件。
<template>
<div>
<button @click="downloadFile">下载文件到指定目录</button>
</div>
</template>
<script>
const { ipcRenderer } = require('electron');
export default {
methods: {
async downloadFile() {
// 指定文件 URL 和文件名
const url = 'https://example.com/path/to/file';
const filename = 'downloaded-file.ext';
// 发送 IPC 消息到主进程
ipcRenderer.send('download-file', { url, filename });
// 监听主进程的回复
ipcRenderer.on('download-file-reply', (event, args) => {
if (args.success) {
console.log('文件下载成功:', args.filePath);
// 可以在这里处理文件下载成功的逻辑,比如显示通知等
} else {
console.error('文件下载失败:', args.error);
// 可以在这里处理文件下载失败的逻辑
}
});
},
},
// ... 其他代码 ...
};
</script>
遇到的问题
问题1:.store
目录像一个缓存或临时目录。
error in ./node_modules/.store/electron@13.6.9/node_modules/electron/index.js
Module not found: Error: Can't resolve 'fs' in 'D:\VSCodeProject\smart_screen\node_modules\.store\electron@13.6.9\node_modules\electron'
error in ./node_modules/.store/electron@13.6.9/node_modules/electron/index.js
Module not found: Error: Can't resolve 'path' in 'D:\VSCodeProject\smart_screen\node_modules\.store\electron@13.6.9\node_modules\electron'
解决:
1、删除
node_modules
目录和package-lock.json
(或yarn.lock
)文件
2、运行npm cache clean --force清除npm缓存,不然直接
npm install重新安装依赖还是一样问题
3、重新运行
npm install
来安装依赖项
问题2:Webpack默认配置不支持这些模块
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }'
- install 'path-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "path": false }
对于path
模块,使用path-browserify
作为polyfill
安装polyfills:
npm install path-browserify --save
或者,如果使用yarn:
yarn add path-browserify
配置Webpack
在您的Webpack配置文件中(通常是webpack.config.js,vue项目在vue.config.js
),您需要添加一个resolve.fallback
条目来指定当Webpack无法解析path
模块时应该使用哪个polyfill
// vue.config.js
module.exports = {
configureWebpack: {
resolve: {
// 你的 Webpack resolve 配置项
}
},
// 其他配置项...
};
后面还遇到一些其他奇奇怪怪的,记录这个的时候忘记了,就不去想了,下面直接贴代码
完整代码
vue.config.js
const { defineConfig } = require('@vue/cli-service');
module.exports = defineConfig({
transpileDependencies: true,
pluginOptions: {
electronBuilder: {
preload: 'src/preload.js', // Specify the path to your preload script
customFileProtocol: "./", // 静态资源路径矫正(不配置打包后,路径不正确加载失败)
}
},
configureWebpack: {
resolve: {
// ... 您的其他resolve配置 ...
fallback: {
"path": require.resolve("path-browserify")
},
}
}
});
preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electron', {
downloadFile: async (options) => {
try {
const { url, filename } = options;
return await ipcRenderer.invoke('download-file', { url, filename });
} catch (error) {
console.error('下载文件时出错:', error);
return { success: false, error: error.message };
}
}
});
background.js
const { app, protocol, BrowserWindow, ipcMain } = require('electron')
const { createProtocol } = require('vue-cli-plugin-electron-builder/lib')
const fs = require('fs');
import axios from 'axios';
const path = require('path');
// 指定目录
const specifiedDirectory = path.join(__dirname, 'src/assets/img');
// 确保目录存在
if (!fs.existsSync(specifiedDirectory)) {
fs.mkdirSync(specifiedDirectory, { recursive: true });
}
async function createWindow() {
// Create the browser window.
const win = new BrowserWindow({
width: 1280,
height: 800,
webPreferences: {
nodeIntegration: false, //process.env.ELECTRON_NODE_INTEGRATION, // 设置为 false,禁用 Node.js 集成
contextIsolation: true,//!process.env.ELECTRON_NODE_INTEGRATION, // 启用上下文隔离
webSecurity: false,//允许跨域
preload: path.join(__dirname, 'preload.js')
}
})
}
app.on('ready', async () => {
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
//await installExtension(VUEJS_DEVTOOLS)
} catch (e) {
console.error('Vue Devtools failed to install:', e.toString())
}
}
createWindow()
ipcMain.handle('download-file', async (event, options) => {
try {
// options 应该包含 url 和文件名
const { url, filename } = options;
const filePath = path.join(specifiedDirectory, filename);
// 使用 axios 下载文件
const response = await axios.get(url, { responseType: 'stream' });
const writer = fs.createWriteStream(filePath);
return new Promise((resolve, reject) => {
response.data.pipe(writer);
let error = null;
writer.on('error', err => {
error = err;
writer.close();
reject(err);
});
writer.on('close', () => {
if (!error) {
resolve({ success: true, filePath }); // 返回成功信息
} else {
reject({ success: false, error: error.message }); // 返回错误信息
}
});
});
} catch (error) {
console.error('下载文件时出错:', error);
event.reply('download-file-reply', { success: false, error: error.message });
}
});
})
上面background.js这里搞错了,应该是return Promise.reject才对
try{
} catch (error) {
console.error('下载文件时出错:', error);
return Promise.reject('download-file-reply', { success: false, error: error.message
});
调用的js文件
this.download('http://www.***.***.com/img/logo/202312/1221_165322_HQz6.png', 'file.png');
/**
* 文件下载指定目录(background.js: 'src/assets/img')
* @param {*} fileUrl 待下载文件url
* @param {*} fileName 文件保存名
*/
async download(fileUrl, fileName) {
const options = {
url: fileUrl,
filename: fileName
};
const result = await window.electron.downloadFile(options);
if (result.success) {
console.log('文件下载成功:', result.filePath);
} else {
console.error('文件下载失败:', result.error);
}
},
后文:electron小白记录,文章有什么用语还是其他不对的地方欢迎斧正。大家一起进步,冲冲冲!