不依赖第三方库在Node.js中解析并保存Multipart表单数据
问题分析与解决方案
你的核心问题在于用字符串处理二进制文件数据导致编码损坏,同时正则提取的文件内容可能包含多余的尾部字符,最终导致保存的文件无效。以下是具体修复步骤:
关键问题点
- 请求体收集方式错误:你直接将
req.body作为字符串处理,但HTTP multipart表单中的文件是二进制数据,转成UTF-16字符串会丢失部分字节,导致文件损坏。 - 正则提取的内容包含冗余字符:原正则匹配到的
contentData可能包含尾部的\r\n和边界分隔符残留,写入文件后会破坏文件结构。 - 文件写入编码错误:使用
"binary"编码写入字符串形式的二进制数据,无法还原原始字节。
修复后的完整实现
1. 正确收集请求体(用Buffer而非字符串)
HTTP请求是流,必须用Buffer数组收集二进制数据,避免编码损失:
const http = require('http'); const fs = require('fs'); const { Buffer } = require('buffer'); http.createServer((req, res) => { if (req.method === 'POST' && req.headers['content-type']?.startsWith('multipart/form-data')) { const bodyChunks = []; req.on('data', chunk => bodyChunks.push(chunk)); req.on('end', () => { const bodyBuffer = Buffer.concat(bodyChunks); const parsedData = parseMultipartFormData(bodyBuffer); res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify(parsedData)); }); } else { res.writeHead(404); res.end(); } }).listen(3000);
2. 重写Multipart解析函数(基于Buffer处理)
用Buffer分割和提取数据,避免字符串转码损坏:
function parseMultipartFormData(bodyBuffer) { // 从Buffer头部提取边界字符串 const boundaryStr = bodyBuffer.toString('utf8', 0, bodyBuffer.indexOf('\r\n')); const boundary = Buffer.from(boundaryStr + '\r\n'); const parts = splitBuffer(bodyBuffer, boundary); parts.shift(); // 移除开头空部分 parts.pop(); // 移除结尾的边界结束标记 const parsedData = {}; for (const part of parts) { // 分割头部和内容:找到第一个\r\n\r\n的位置 const headerEndIdx = part.indexOf(Buffer.from('\r\n\r\n')); if (headerEndIdx === -1) continue; const headerStr = part.toString('utf8', 0, headerEndIdx); const contentBuffer = part.slice(headerEndIdx + 4); // 跳过\r\n\r\n if (headerStr.includes('filename')) { // 解析文件字段 const nameMatch = headerStr.match(/name="(.+)"/); const filenameMatch = headerStr.match(/filename="(.+)"/); const contentTypeMatch = headerStr.match(/Content-Type: (\w+\/\w+)/); if (nameMatch && filenameMatch && contentTypeMatch) { // 移除内容末尾的\r\n(避免写入文件时添加冗余字节) const cleanContent = contentBuffer.slice(0, contentBuffer.length - 2); parsedData[nameMatch[1]] = { filename: filenameMatch[1], contentType: contentTypeMatch[1], contentData: cleanContent, }; // 直接写入Buffer,无需指定编码 fs.writeFile(filenameMatch[1], cleanContent, (err) => { err ? console.error('保存失败:', err) : console.log('文件保存成功'); }); } } else { // 解析普通文本字段 const nameMatch = headerStr.match(/name="(.+)"/); if (nameMatch) { parsedData[nameMatch[1]] = contentBuffer.toString('utf8').trim(); } } } return parsedData; } // 辅助函数:用分隔符分割Buffer function splitBuffer(buffer, separator) { const parts = []; let start = 0; let index; while ((index = buffer.indexOf(separator, start)) !== -1) { parts.push(buffer.slice(start, index)); start = index + separator.length; } parts.push(buffer.slice(start)); return parts; }
核心修复说明
- 用Buffer处理二进制数据:全程避免将文件数据转成字符串,保留原始字节。
- 精准分割内容:自定义
splitBuffer函数分割multipart部分,避免字符串split的编码问题。 - 清理冗余字符:移除文件内容末尾的
\r\n,保证文件结构完整。 - 直接写入Buffer:
fs.writeFile支持直接写入Buffer,自动处理字节,无需指定编码。
内容的提问来源于stack exchange,提问作者Trikoo
相关产品推荐
相关产品推荐

