如何仅用vanilla Node.js构造含二进制文件的multipart/form-data报文
Code by Zapier 无依赖构造multipart/form-data报文方案
有完全基于 vanilla Node.js 即可实现的方案,无需依赖任何第三方npm包,核心逻辑是手动拼接符合HTTP协议规范的multipart/form-data报文。
实现原理
multipart/form-data报文结构遵循固定的HTTP协议规则,只要按规则拼接即可:
- 首先生成一个唯一的边界字符串(boundary),作用是分隔不同的表单字段,需保证不会出现在任何字段的内容中
- 每个表单字段的结构固定:
--+ 边界字符串 + CRLF换行 + 字段头描述 + 两次CRLF换行 + 字段值 + CRLF换行 - 所有字段拼接完成后,末尾需要拼接
--+ 边界字符串 +--+ CRLF作为报文结束标识 - 二进制文件类型的字段,需要额外在头信息中指定filename属性和对应Content-Type,内容直接传入二进制Buffer即可
可直接在Code by Zapier中运行的示例代码
const http = require('http'); const fs = require('fs'); // 仅读取本地文件时使用,Zapier中可直接用步骤传入的Buffer // 1. 生成唯一边界字符串 const BOUNDARY = `----ZapierFormBoundary${Date.now()}${Math.random().toString(16).slice(2)}`; const CRLF = '\r\n'; // 2. 准备要提交的字段:示例包含普通文本字段和二进制文件字段 const formFields = [ // 普通文本字段 { name: 'username', value: 'test_user' }, // 二进制文件字段 { name: 'avatar', filename: 'avatar.png', contentType: 'image/png', // 这里替换为你实际拿到的文件Buffer,比如Zapier触发步骤传入的文件二进制 value: fs.readFileSync('./test_avatar.png') } ]; // 3. 拼接表单报文 const chunks = []; for (const field of formFields) { // 拼接字段开头边界和头信息 chunks.push(Buffer.from(`--${BOUNDARY}${CRLF}`)); if (field.filename) { // 文件字段头 chunks.push(Buffer.from(`Content-Disposition: form-data; name="${field.name}"; filename="${field.filename}"${CRLF}`)); chunks.push(Buffer.from(`Content-Type: ${field.contentType}${CRLF}`)); } else { // 普通文本字段头 chunks.push(Buffer.from(`Content-Disposition: form-data; name="${field.name}"${CRLF}`)); chunks.push(Buffer.from(`Content-Type: text/plain; charset=utf-8${CRLF}`)); } // 头和值之间的空行 chunks.push(Buffer.from(CRLF)); // 字段值 chunks.push(Buffer.isBuffer(field.value) ? field.value : Buffer.from(field.value)); // 字段结尾换行 chunks.push(Buffer.from(CRLF)); } // 拼接整个表单的结束边界 chunks.push(Buffer.from(`--${BOUNDARY}--${CRLF}`)); const fullBody = Buffer.concat(chunks); // 4. 发送请求示例 const options = { hostname: '你的目标接口域名', port: 80, path: '你的接口路径', method: 'POST', headers: { 'Content-Type': `multipart/form-data; boundary=${BOUNDARY}`, 'Content-Length': fullBody.length } }; const req = http.request(options, (res) => { let resData = ''; res.on('data', (chunk) => { resData += chunk; }); res.on('end', () => { console.log('请求成功', resData); // Zapier中可以直接output结果给后续步骤使用 output = { success: true, response: resData }; }); }); req.on('error', (e) => { console.error('请求出错', e); output = { success: false, error: e.message }; }); req.write(fullBody); req.end();
注意事项
- 边界字符串建议用时间戳加随机字符串拼接生成,避免和表单内容重复导致解析失败
- 所有换行必须使用
\r\n(CRLF),不能用\n,否则大部分服务端会无法正常解析表单 - 二进制内容直接传入Buffer即可,不要调用toString()方法做编码转换,否则会导致文件损坏
- 如果使用Zapier内置的
fetch方法发送请求,直接将拼接好的fullBody作为body参数传入,对应配置headers即可
内容的提问来源于stack exchange,提问作者bdasliva83
相关产品推荐
相关产品推荐

