前言 之前想做一个接受文件保存到本地的node服务,查遍了全网大部分都是浅尝辄止,点到为止。胡乱贴两段代码完事。经过一段时间的摸索写了一个完整可用的服务,希望能为有相关需要的开发提供一点思路。
效果
我们前端传输文件一般采用formdata的格式,所以服务端的核心在于怎样去解析formdata格式的数据,目前比较流行的解析插件主要有multiparty与 busboy ,但我看了下大部分的资料都是围绕multiparty的,所以我这边也是采用multiparty进行数据解析。
在引入插件
npm install multiparty
之后,最简单的方法就是直接将需要保存文件的路径传入插件,即可自动解析保存(注意文件路径必须存在不然会报错)
(req, res)=>{
var form = new multiparty.Form({uploadDir:'保存文件的地址'})
// 进行解析
form.parse(req)
}
但这种方法插件会将文件先保存到内存然后在从内存保存到硬盘,我们可以直接通过创建流来将数据写入硬盘。
监听part事件
Emitted when a part is encountered in the request. part is a ReadableStream. It also has the following propertie
获取文件可读流,然后根据文件路径创建写入流,然后将文件流写入。
完整代码
const express = require('express');
const multiparty = require('multiparty');
const app = express();
const fs = require('fs');
const path = require('path')
const Busboy = require('busboy');
app.get('/', (req, res) => {
res.send('hello, this is a node server for upload file current env is' + process.env.NODE_ENV)
})
// 跨域
app.use(function (req, res, next) {
if (req.method == 'OPTIONS') res.send('OPTIONS PASS')
res.append('Access-Control-Allow-Origin', '*')
res.append('Access-Control-Allow-Methods', 'OPTIONS, GET, PUT, POST, DELETE')
res.append('Access-Control-Allow-Headers', '*')
next();
});
// 上传
app.post('/upload', (req, res) => {
const form = new multiparty.Form();
try {
form.on('part', async function (part) {
if (part.filename) {
// 获取上传路径
const p = await new Promise((resolve, reject) => {
form.on('field', (name, value) => {
resolve(name == 'path'?value:'')
})
})
const saveTo = (process.env.NODE_ENV == 'dev' ? 'D://Temp' : '/usr/static') + (p || '/default')
// 如果不存在文件夹路径则创建文件夹
await new Promise((resolve, reject) => {
fs.stat(saveTo, (err) => {
if (err) {
fs.mkdirSync(saveTo);
}
resolve()
})
})
// 根据路径创建写入流
const writeStrem = fs.createWriteStream(path.join(saveTo, part.filename))
part.pipe(writeStrem)
}
part.on('error', function (err) {
fileStrem.destroy();
});
});
form.parse(req)
} catch (e) {
console.log(e)
res.send('500')
}
res.send('200')
})
app.listen(8010, function () {
console.log('Example app listening on port 8010!\n');
});
使用busboy同理,也是将文件流进行写入
const express = require('express');
const multiparty = require('multiparty');
const app = express();
const fs = require('fs');
const path = require('path')
const Busboy = require('busboy');
app.get('/', (req, res) => {
res.send('hello, this is a node server for upload file current env is' + process.env.NODE_ENV)
})
// 跨域
app.use(function (req, res, next) {
if (req.method == 'OPTIONS') res.send('OPTIONS PASS')
res.append('Access-Control-Allow-Origin', '*')
res.append('Access-Control-Allow-Methods', 'OPTIONS, GET, PUT, POST, DELETE')
res.append('Access-Control-Allow-Headers', '*')
next();
});
// 上传
app.post('/upload', (req, res) => {
// const form = new multiparty.Form();
// try {
// form.on('part', async function (part) {
// if (part.filename) {
// // 获取上传路径
// const p = await new Promise((resolve, reject) => {
// form.on('field', (name, value) => {
// resolve(name == 'path'?value:'')
// })
// })
// const saveTo = (process.env.NODE_ENV == 'dev' ? 'D://Temp' : '/usr/static') + (p || '/default')
// // 如果不存在文件夹路径则创建文件夹
// await new Promise((resolve, reject) => {
// fs.stat(saveTo, (err) => {
// if (err) {
// fs.mkdirSync(saveTo);
// }
// resolve()
// })
// })
// // 根据路径创建写入流
// const writeStrem = fs.createWriteStream(path.join(saveTo, part.filename))
// part.pipe(writeStrem)
// }
// part.on('error', function (err) {
// fileStrem.destroy();
// });
// });
// form.parse(req)
// } catch (e) {
// console.log(e)
// res.send('500')
// }
const busboy = new Busboy({ headers: req.headers });
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
var saveTo = path.join('D://Temp', path.basename(filename));
file.pipe(fs.createWriteStream(saveTo));
});
busboy.on('finish', function() {
res.writeHead(200, { 'Connection': 'close' });
res.end("That's all folks!");
});
return req.pipe(busboy);
})
app.listen(8010, function () {
console.log('Example app listening on port 8010!\n');
});