You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

错误原因

  1. Node.js环境不兼容Blob对象:Blob是浏览器API,主进程为Node.js环境,直接使用会导致FormData无法正确处理流,触发source.on is not a function。
  2. axios参数包装错误:将FormData包裹在{ ...params }对象中,导致axios尝试重新转换FormData,而非直接传递原始数据。
  3. 异步代码混用:fs.readFile回调与async/await混用,流程混乱;且无需转Blob,Node.js原生Buffer/文件流更适合。
  4. 请求头设置错误:手动设置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 05:07:17