直接给下Node.js后端两种文件上传方式、后端服务接收保存文件,以及后端文件下载的具体代码实现。
一、Node.js后端文件上传
这边注意下,如果出现以下错误
Error: socket hang up
检查下上传的后端接口是不是用https,之后修改下引入的http对象为https。
1)使用FormData上传文件
var FormData = require('form-data') //如果后端用的是https,这边引入的是http就会报socket hang up异常。 var http = require('https'); //调用示例: let form = new FormData(); var uploadLoadFilePath = "../../test.zip" form.append("filename", fs.createReadStream(uploadLoadFilePath)) uploadRequest("test.cn", "8080" '/api/upload/file', form, 'POST').then(function (result) { }).catch(function (err) { }) /** * 上传文件或者请求数据都可以用这个方法处理 */ function uploadRequest(host, port, path, body, method){ return new Promise(function (resolve, reject) { var option = { hostname: host, port: port, path: path, method: method ? method : 'GET', headers: {} }; var data = null; if (body) { if (body instanceof FormData) { option.method = 'POST'; option.headers = body.getHeaders() data = body } else { if (typeof(body) == 'string') { data = body } else { data = JSON.stringify(body) } option.method = 'POST'; option.headers["Content-Length"] = Buffer.byteLength(data, 'utf8'); option.headers["Content-Type"] = 'application/json'; } } if (method) { option.method = method; } var req = http.request(option, function (res) { var body = ''; res.on('data', function (chunk) { body += chunk; }); res.on('end', function () { console.log("coverage coverageRequest request body:%s", body) resolve(JSON.parse(body)); }) }); req.on('error', function (e) { console.log("coverage coverageRequest request error:%s", e) reject(e) }); if (data instanceof FormData) { data.pipe(req) } else { if (data != null) { req.write(data); } req.end(); } }); }
2)自定义boundary参数上传文件
// 示例调用 let body = { "testType": testType, "flag": 'full_test' } var savePath = "../../test.zip" uploadFiles("test.cn", '/api/upload/file', "8080", [{urlKey: 'testData', urlValue: JSON.stringify(body)}], [{urlKey: 'file', urlValue: savePath}]).then(function (result) { console.log("uploadFile result:%s", result) }).catch(function (err) { console.log("uploadFile err:%s", err) }) /** * 文件上传 */ function uploadFiles(host, path, port, fileDataInfo, fileKeyValue) { var options = { hostname: host, port: port?port:80, path: path, method: 'POST' } return new Promise(function(resolve, reject) { var req = http.request(options, function(res) { var data = '' res.on("data", function(chunk) { data += chunk; }) res.on('end', () => { try{ resolve(data) }catch (err){ reject(err) } }) }).on("error", function (err) { reject(err) }) var boundaryKey = Math.random().toString(16); var enddata = '\r\n----' + boundaryKey + '--'; var dataLength = 0; var dataArr = new Array(); for (var i = 0; i < fileDataInfo.length; i++) { var dataInfo = "\r\n----" + boundaryKey + "\r\n" + "Content-Disposition: form-data; name=\"" + fileDataInfo[i].urlKey + "\"\r\n\r\n" + fileDataInfo[i].urlValue; var dataBinary = new Buffer(dataInfo, "utf-8"); dataLength += dataBinary.length; dataArr.push({ dataInfo: dataInfo }); } 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=\"" + pathUtil.basename(fileKeyValue[i].urlValue) + "\"\r\n" + "Content-Transfer-Encoding: binary\r\n\r\n"; var contentBinary = new Buffer(content, 'utf-8'); files.push({ contentBinary: contentBinary, filePath: fileKeyValue[i].urlValue }); } var contentLength = 0; for (var i = 0; i < files.length; i++) { var filePath = files[i].filePath; if (fs.existsSync(filePath)) { var stat = fs.statSync(filePath); contentLength += stat.size; } else { contentLength += new Buffer("\r\n", 'utf-8').length; } contentLength += files[i].contentBinary.length; } req.setHeader('Content-Type', 'multipart/form-data; boundary=--' + boundaryKey); req.setHeader('Content-Length', dataLength + contentLength + Buffer.byteLength(enddata)); // 将参数发出 for (var i = 0; i < dataArr.length; i++) { req.write(dataArr[i].dataInfo) //req.write('\r\n') } var fileindex = 0; var doOneFile = function() { req.write(files[fileindex].contentBinary); var currentFilePath = files[fileindex].filePath; if (fs.existsSync(currentFilePath)) { var fileStream = fs.createReadStream(currentFilePath, {bufferSize: 4 * 1024}); fileStream.pipe(req, {end: false}); fileStream.on('end', function() { fileindex++; if (fileindex == files.length) { req.end(enddata); } else { doOneFile(); } }); } else { req.write("\r\n"); fileindex++; if (fileindex == files.length) { req.end(enddata); } else { doOneFile(); } } }; if (fileindex == files.length) { req.end(enddata); } else { doOneFile(); } }) }
二、Node.js后端文件接收及保存
这边用formidable来解析获取文件名和服务端保存的文件路径。
var formidable = require('formidable') var Promise = require('bluebird')
app.post('/api/upload/file', function (req, res) { var form = new formidable.IncomingForm() if (options.saveDir) { form.uploadDir = options.saveDir } Promise.promisify(form.parse, form)(req) .spread(function(fields, files) { return Object.keys(files).map(function(field) { var file = files[field] //file.name字段获取文件名,file.path字段获取服务端保存的文件路径,这边可以把文件移动存储到你想要的位置 console.log(">>>>>>>fileName:%s, filePath:%s", file.name, file.path); }) }).catch(function(err) { res.status(500) .json({ success: false , error: 'ServerError' }) }) });
三、Node.js后端文件下载
//示例调用 donwloadFile("test.cn", "80", "/test/test.zip", "/User/Download/temp/test.zip") function downloadFile(host, port, path, downloadPath) { var writeStream = fs.createWriteStream(downloadPath); var option = { hostname: host, port: port, method: 'GET', path: path } var req = http.request(option, function (res) { if (res.statusCode == 200) { res.pipe(writeStream); res.on('data', (chunk) => { }); res.on("end", function () { }); } else { } }); req.on("error", function (err) { }); req.end(); }
如果后端服务用的是https,在做文件下载操作的时候端口用80就会报如下异常:
error:write EPROTO 140036986775360:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO: unknown protocol:../deps/openssl/openssl/ssl/s23_clnt.c:827:
此时端口要改为443,即:
donwloadFile("test.cn", "443", "/test/test.zip", "/User/Download/temp/test.zip")
扩展阅读:
转载请注明出处:陈文管的博客 – Node.js后端文件上传、文件接收保存及文件下载实现
扫码或搜索:文呓
微信公众号 扫一扫关注