如何用Electron+Axios向接口上传本地ZIP文件?
Electron + Axios 上传ZIP文件报错 TypeError: source.on is not a function
问题场景
在Electron 26.2.1主进程的异步代码中,使用axios 1.7.7向接口上传本地ZIP文件(接口接收文件+字符串哈希值),Postman中请求正常,但代码执行时报错。
原sendConf方法代码
static async sendConf() { const dataFolder = process.env.NODE_ENV === 'development' ? path.join(__dirname, '../../data') : path.join(app.getPath('userData'), '/data'); await Utilities.zipDirectory( dataFolder, path.join(dataFolder, 'conf.zip'), ).then(async (r) => { const fs = require('fs'); const form_data = new FormData(); fs.readFile(path.join(dataFolder, 'conf.zip'), async (error, data) => { form_data.append('hash', TOKEN); const bb = new Blob([data]); form_data.append('file', bb, { contentType: 'multipart/form-data', filename: 'conf.zip', }); const response = await Utilities.postAxios('upload-conf', { data: form_data, }) .then((r) => { console.log(`done`); }) .catch((e) => { console.log(`sendConf err : ${e}`); }); console.log(`sendConf : ${response}`); }); }); }
原postAxios方法代码
static async postAxios(url, params) { const obj = await Utilities.readConfFile('settings'); const authAxios = axios.create({ baseURL: obj.generalSettings.warehouse, }); try { const { data } = await authAxios.post( url, { ...params }, { headers: { 'Content-Type': 'multipart/form-data', }, }, ); return data; } catch (error) { console.log(error); } }
报错信息
TypeError: source.on is not a function at Function.DelayedStream.create (/project/node_modules/delayed-stream/lib/delayed_stream.js:33:10) at FormData.CombinedStream.append (/project/node_modules/combined-stream/lib/combined_stream.js:45:37) at FormData.append (/project/node_modules/form-data/lib/form_data.js:75:3) at FormData.defaultVisitor (/project/node_modules/axios/lib/helpers/toFormData.js:175:14) at each (/project/node_modules/axios/lib/helpers/toFormData.js:198:73) at Object.forEach (/project/node_modules/axios/lib/utils.js:267:10) at build (/project/node_modules/axios/lib/helpers/toFormData.js:197:11) at toFormData (/project/node_modules/axios/lib/helpers/toFormData.js:214:3) at Object.transformRequest (/project/node_modules/axios/lib/defaults/index.js:84:16) at transform (/project/node_modules/axios/lib/core/transformData.js:22:15) at Axios.request (/project/node_modules/axios/lib/core/Axios.js:45:41) at async Function.postAxios (/project/src/main/utilities.ts:114:24) at async /project/src/main/utilities.ts:297:26
错误原因
- Node.js环境不兼容Blob对象:Blob是浏览器API,主进程为Node.js环境,直接使用会导致FormData无法正确处理流,触发
source.on is not a function。 - axios参数包装错误:将FormData包裹在
{ ...params }对象中,导致axios尝试重新转换FormData,而非直接传递原始数据。 - 异步代码混用:
fs.readFile回调与async/await混用,流程混乱;且无需转Blob,Node.js原生Buffer/文件流更适合。 - 请求头设置错误:手动设置
Content-Type: multipart/form-data但缺少boundary参数,导致请求格式异常。
修复方案
1. 修改sendConf方法(统一async/await,用Buffer替代Blob)
static async sendConf() { const dataFolder = process.env.NODE_ENV === 'development' ? path.join(__dirname, '../../data') : path.join(app.getPath('userData'), '/data'); const zipPath = path.join(dataFolder, 'conf.zip'); // 等待压缩完成 await Utilities.zipDirectory(dataFolder, zipPath); const fs = require('fs').promises; const FormData = require('form-data'); const form_data = new FormData(); // 用async/await读取文件Buffer const fileBuffer = await fs.readFile(zipPath); form_data.append('hash', TOKEN); // 直接传入Buffer,指定正确的文件MIME类型 form_data.append('file', fileBuffer, { filename: 'conf.zip', contentType: 'application/zip' }); try { const response = await Utilities.postAxios('upload-conf', form_data); console.log(`done`); console.log(`sendConf :`, response); } catch (e) { console.log(`sendConf err :`, e); } }
2. 修改postAxios方法(直接传递FormData,自动生成请求头)
static async postAxios(url, formData) { const obj = await Utilities.readConfFile('settings'); const authAxios = axios.create({ baseURL: obj.generalSettings.warehouse, }); try { // 直接传递FormData,用form-data的getHeaders()生成正确的请求头(含boundary) const { data } = await authAxios.post(url, formData, { headers: { ...formData.getHeaders() } }); return data; } catch (error) { console.log(error); throw error; // 抛出错误让上层处理 } }
额外注意事项
- 确保安装
form-data包:执行npm install form-data(Node.js环境必须,浏览器环境无需)。 - 不要手动设置
Content-Type: multipart/form-data,formData.getHeaders()会自动生成包含boundary的正确请求头。 - ZIP文件的MIME类型用
application/zip,而非multipart/form-data(后者是整个请求的类型)。
内容的提问来源于stack exchange,提问作者khalidgt
相关产品推荐
相关产品推荐

