eggjs 自身的multipart 解析
eggis 有两大特点:
- 懒解析:只有在主动调用时, 比如
this.multipart, 才会解析multipart 请求。 - 流式stream处理:如果想再拿到fd 文件前得到fields, fd 就必须放在fields 之后。
这可能是出于性能和安全的考虑吧。但是这也给开发带来了不便。
- 处理 fd 之前,不能提前获得fields
- fd stream 必须消费掉,否则连接不会关闭, 浏览器pending.
统一处理
可以参考下面的方法,统一解析出 fields, 并消费掉stream.
// app/core/base_controller.js
// app/core/base_controller.js
const tmp = require('tmp')
const fs = require('fs')
const app = require('egg');
module.exports =
app.BaseController =
class BaseController extends app.Controller {
rest(rest) {
this.ctx.body = typeof rest === 'object' ? rest : JSON.stringify(rest)
this.ctx.type = 'application/json';
}
throw(msg) {
msg = msg || 'unknown';
this.ctx.throw(400, msg);
}
buildURLQuery(obj){
return Object.entries(obj)
.map(pair => pair.map(encodeURIComponent).join('='))
.join('&');
}
async multipart() {
const ctx = this.ctx
const files = [];
ctx.files = files
let fields, part;
if (this.ctx.get('Content-Type').startsWith('multipart/')) {
try {
// const file = await ctx.getFileStream()
// files.push(file);
// fields = stream.fields;
const sendToWormhole = require('stream-wormhole');
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)
//let result = await ctx.oss.put('egg-multipart-test/' + part.filename, part);
//await sendToWormhole(part);
}
}
fields = parts.field
} catch (err) {
//await sendToWormhole(part);
throw err;
}
}
return [files, fields];
}
}