获取前端上传的文件
如果是单个文件,就看官方文档够了:https://eggjs.org/zh-cn/basics/controller.html#%E8%8E%B7%E5%8F%96%E4%B8%8A%E4%BC%A0%E7%9A%84%E6%96%87%E4%BB%B6
如果是多个文件,会有坑,继续往下看
PS:egg.js为了安全起见,有一些文件格式需要在配置文件中扩展才能上传,否则会失败,例如证书文件.p12
config.multipart = {
fileExtensions: [ '.p12' ] // 增加对 p12 扩展名的文件支持
};
需求
当时遇到坑时的需求,在公司加班到晚上十二点多也没解决,第二天才解决。
- 从前端获取多个文件和多个参数
- 使用参数生成唯一标识
- 以唯一标识为目录将文件上传至阿里云OSS
问题
虽然官方文档提供了获取的方法。但是因为需求比较特殊,需要从前端获取多个文件和参数,然后需要一次性取完多个文件和参数,然后代码通过参数生成一些数据后才真正使用文件。
官方文档中多文件流的获取方式是
const parts = ctx.multipart();
while ((part = await parts()) != null) {
...
}
。每次调用则获取一个参数或者一个文件流。使用while语句调用即可调用完。
浏览器pending问题
但是这时候出现了问题,每次调用parts()方法后获取的part需要被消费掉。不消费掉将会导致浏览器卡死。这里的消费好像是要传到OSS或者之类的操作,然后我先将它放在了一个数组中,这样并不是消费掉。于是需要手动消费await sendToWormhole(part);
文件销毁问题
但是经过测试,经过了await sendToWormhole(part);
方法后,存在数组中的文件流也被销毁了。所以这个multipart()
方法不了了之
POST的参数问题
上传文件时,还需要前端提供一些POST参数,文件需要form-data
格式,所以POST参数也使用form-data格式。但是node.js框架默认获取的POST是x-www-form-urlencoded
格式。所以POST参数默认传不上,使用上面的parts()
也可以获取到参数,但现在着重处理文件问题。
补充:如果使用了parts()方法,在获取参数的时候可以设置成统一获取:
const parts = ctx.multipart({ autoFields: true });
while ((part = await parts()) != null) {
if (part.length) {
} else if (part.filename) {
const tmpFile = tmp.fileSync({prefix: 'eggjs-upload-'})
const ws = fs.createWriteStream(null, {fd:tmpFile.fd})
part.pipe(ws)
part.path = tmpFile.name
part.fd = tmpFile.fd
files.push(part)
}
}
fields = parts.field
注意代码中的autoFields: true
和fields = parts.field
。
解决方案
从网上找到了一个方案,是在中间件中对header进行判断,然后将文件进行临时存储,类似于PHP中上传文件了。
新建中间件
/*
*中间件,作用是把表单方式上传的参数和文件预处理后再写入request.body和ctx.request.upload_files,相当于post数据了
*/
const formidable = require("formidable");
module.exports = options => {
return async function multipart_upload(ctx, next) {
var params = {} ;
if (ctx.request.header['content-type'] && ctx.request.header['content-type'].indexOf("multipart") > -1 ){
var parse = async function(req) {
const form = new formidable.IncomingForm();
return new Promise((resolve, reject) => {
form.parse(req, (err, fields, files) => {
resolve({ fields, files })
})
});
}
const extraParams = await parse(ctx.req);
if (extraParams) {
if (extraParams.fields) {
//将参数合并到body中,这样就可以相当于post数据了
ctx.request.body = Object.assign(ctx.request.body,extraParams.fields);
};
if (extraParams.files) {
//将上传文件信息合并,这样在控制器里就可以通过ctx.request.upload_files获取了
ctx.request.upload_files = extraParams.files ;
};
};
}
await next(); //执行下一步
};
};
在Controller层获取
let name = ctx.request.upload_files.name;
let path = name.path;
egg.js还是这么多坑啊!!!!
github: https://github.com/Hikiy
作者:Hiki
创建日期:2019.07.03
更新日期:2019.07.03
(转载本站文章请注明作者和出处 Hiki)