eggjs 自身的multipart 解析
eggis 有两大特点:
- 懒解析:只有在主动调用时, 比如
this.multipart
, 才会解析multipart 请求。 - 流式stream处理:如果想在拿到fd 文件前得到fields, fd 就必须放在fields 之后。
Stream 能让server 性能更好。但是这也给开发带来了不便。
- 处理 fd 之前,不能提前获得fields
- fd stream 必须消费掉,否则连接不会关闭, 浏览器pending.
统一处理
可以参考下面的方法,统一解析出 fields, 并消费掉stream.
// app/core/base_controller.js
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);
}
async multipart() {
const tmp = require('tmp')
const fs = require('fs')
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');
// let result = await ctx.oss.put('egg-multipart-test/' + part.filename, part);
// await sendToWormhole(part);
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
} catch (err) {
//await sendToWormhole(part);
throw err;
}
}
return [files, fields];
}
}
使用时,直接可获取到上传的文件+fields:
const [files, fields] = await this.multipart();
另外为了及时清理临时文件,我设置了 ctx.files
存放临时文件。
然后通过中间件middleware 在请求最后及时清理:
if (ctx.files && ctx.files.length) {
for (let file of ctx.files) {
if (file.path && fs.existsSync(file.path)) {
fs.unlinkSync(file.path)
}
}
}
加载baseController
显然,默认情况下,node 不支持项目根目录加载:
require('core/base_controller')
我们需要在app.js 里面设置:NODE_PATH
let path = require('path')
process.env.NODE_PATH = path.resolve(__dirname, 'app/') ;
require('module').Module._initPaths();
或者在命令行直接设置: NODE_PATH=$PWD/app egg-bin dev
vscode 自动补全
vscode 可不知道你设置了NODE_PATH, 所以 vsc 也找不到module. 我们需要再项目根目录中通过jsconfig.json
告诉vsc 去base_dir 找module.
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"baseUrl": "./app"
},
"exclude": [
"node_modules"
]
}