本文介绍如何使用 Node.js 原生 http 模块模拟 Form 表单提交文件。通过构造 multipart/form-data 格式的请求体,包含边界符、文件元信息及二进制数据,实现与服务端的文件交互。文中提供了完整的客户端发送代码、服务端接收保存逻辑以及测试脚本,并指出大文件上传可能涉及服务器配置问题。
Node.js 模拟 Form 表单上传文件实现
此前项目中曾包含此功能,近期在客户现场使用时出现故障。据同事反馈,该功能一直未正常运行。因此,耗时一天完成了此功能的开发与调试(自感耗时较长),特此分享。
以下是相关代码及测试用例:
var http = require('http');
var path = require('path');
var fs = require('fs');
function postFile(fileKeyValue, req) {
var boundaryKey = Math.random().toString(16);
var enddata = '\r\n----' + boundaryKey + '--';
var files = new Array();
for (var i = 0; i < fileKeyValue.length; i++) {
var content = "\r\n----" + boundaryKey + "\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Disposition: form-data; name=\"" + fileKeyValue[i].urlKey + "\"; filename=\"" + path.basename(fileKeyValue[i].urlValue) + "\"\r\n" + "Content-Transfer-Encoding: binary\r\n\r\n";
var contentBinary = new Buffer(content, 'utf-8');//当编码为ascii时,中文会乱码。
files.push({contentBinary: contentBinary, filePath: fileKeyValue[i].urlValue});
}
var contentLength = 0;
for (var i = 0; i < files.length; i++) {
var stat = fs.statSync(files[i].filePath);
contentLength += files[i].contentBinary.length;
contentLength += stat.size;
}
req.setHeader('Content-Type', 'multipart/form-data; boundary=--' + boundaryKey);
req.setHeader('Content-Length', contentLength + Buffer.byteLength(enddata));
// 将参数发出
var fileindex = 0;
var doOneFile = function(){
req.write(files[fileindex].contentBinary);
var fileStream = fs.createReadStream(files[fileindex].filePath, {bufferSize : 4 * 1024});
fileStream.pipe(req, {end: false});
fileStream.on('end', function() {
fileindex++;
if(fileindex == files.length){
req.end(enddata);
} else {
doOneFile();
}
});
};
if(fileindex == files.length){
req.end(enddata);
} else {
doOneFile();
}
}
//测试用例
//http://nodejs.org/api/http.html#http_http_request_options_callback
var files = [
{urlKey: "file1", urlValue: "E:\\DFBF.jpg"},
{urlKey: "file2", urlValue: "E:\\1.jpg"},
{urlKey: "file3", urlValue: "E:\\Pro 空格 中文.mp3"}
]
var options = {
host: "localhost",
port: "8908" ,
method: "POST",
path: "/Home/Upload"
}
var req = http.request(options, function(res){
console.log("RES:" + res);
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
//res.setEncoding("utf8");
res.on("data", function(chunk){
console.log("BODY:" + chunk);
})
})
req.on('error', function(e){
console.log('problem with request:' + e.message);
console.log(e);
})
postFile(files, req);
console.log("done");
服务端测试环节,在 MVC 架构的 Home 控制器中编写了 upload 方法,遍历接收到的文件并将其保存至硬盘。
仅在大文件上传时存在异常,推测需调整服务器配置,此处暂不深入处理。
服务端方法(位于 Home 控制器下)
[HttpPost]
public string Upload()
{
//HttpPostedFileBase file = this.Request.Files["file"];
//file.SaveAs(file.FileName);
foreach (string file in this.Request.Files)
{
this.Request.Files[file].SaveAs(@"E:\新建文件夹\" + this.Request.Files[file].FileName);
}
return @"保存成功 路径:E:\新建文件夹\";
}
运行脚本:
node nodejsPostFile.js
运行结果:
