新人想请教egg-multipart问题:使用File 模式处理文件上传
é®é¢ç°è±¡ï¼æ¹éä¸ä¼ æä»¶æ¶ä¼åºç°æå 个æä»¶å§ç»å¤äºä¸ä¼ ç¶æï¼å¯¹åºçä¸ä¼ 请æ±å¤äºæèµ·ç¶æã BUGææ¥ï¼
// node_modules/egg-multipart/app/extend/context.js
async saveRequestFiles(options) {
options = options || {};
const ctx = this;
const multipartOptions = {
autoFields: false,
};
if (options.defCharset) multipartOptions.defCharset = options.defCharset;
if (options.limits) multipartOptions.limits = options.limits;
if (options.checkFile) multipartOptions.checkFile = options.checkFile;
let storedir;
const requestBody = {};
const requestFiles = [];
const parts = ctx.multipart(multipartOptions);
let part;
do {
try {
part = await parts();
} catch (err) {
await ctx.cleanupRequestFiles(requestFiles);
throw err;
}
if (!part) break;
if (part.length) {
ctx.coreLogger.debug('[egg-multipart:storeMultipart] handle value part: %j', part);
const fieldnameTruncated = part[2];
const valueTruncated = part[3];
if (valueTruncated) {
await ctx.cleanupRequestFiles(requestFiles);
limit('Request_fieldSize_limit', 'Reach fieldSize limit');
}
if (fieldnameTruncated) {
await ctx.cleanupRequestFiles(requestFiles);
limit('Request_fieldNameSize_limit', 'Reach fieldNameSize limit');
}
// arrays are busboy fields
requestBody[part[0]] = part[1];
continue;
}
// otherwise, it's a stream
const meta = {
field: part.fieldname,
filename: part.filename,
encoding: part.encoding,
mime: part.mime,
};
// keep same property name as file stream
// https://github.com/cojs/busboy/blob/master/index.js#L114
meta.fieldname = meta.field;
meta.transferEncoding = meta.encoding;
meta.mimeType = meta.mime;
ctx.coreLogger.debug('[egg-multipart:storeMultipart] handle stream part: %j', meta);
// empty part, ignore it
if (!part.filename) {
await sendToWormhole(part);
continue;
}
if (!storedir) {
// ${tmpdir}/YYYY/MM/DD/HH
storedir = path.join(ctx.app.config.multipart.tmpdir, moment().format('YYYY/MM/DD/HH'));
const exists = await fs.exists(storedir);
if (!exists) {
await mkdirp(storedir);
}
}
const filepath = path.join(storedir, uuid.v4() + path.extname(meta.filename));
const target = fs.createWriteStream(filepath);
await pump(part, target);
// https://github.com/mscdex/busboy/blob/master/lib/types/multipart.js#L221
meta.filepath = filepath;
requestFiles.push(meta);
// https://github.com/mscdex/busboy/blob/master/lib/types/multipart.js#L221
if (part.truncated) {
await ctx.cleanupRequestFiles(requestFiles);
limit('Request_fileSize_limit', 'Reach fileSize limit');
}
} while (part != null);
ctx.request.body = requestBody;
ctx.request.files = requestFiles;
},
导è´è¯¥é®é¢çåå æ¯egg-multipartçsaveRequestFiles()没æçæå¯¹åºçpartæä»¶ã
和 https://github.com/eggjs/egg/issues/4808 这个应该是同一个问题,需要看下
æææ¥åç°æ¯co-busboyçparseæé®é¢
// node_modules/egg-multipart/app/extend/context.js
const parse = require('co-busboy');
multipart(options) {
// multipart/form-data
if (!this.is('multipart')) {
this.throw(400, 'Content-Type must be multipart/*');
}
if (this[HAS_CONSUMED]) throw new TypeError('the multipart request can\'t be consumed twice');
this[HAS_CONSUMED] = true;
const parseOptions = Object.assign({}, this.app.config.multipartParseOptions);
options = options || {};
if (typeof options.autoFields === 'boolean') parseOptions.autoFields = options.autoFields;
if (options.defCharset) parseOptions.defCharset = options.defCharset;
if (options.checkFile) parseOptions.checkFile = options.checkFile;
// merge and create a new limits object
if (options.limits) {
const limits = options.limits;
for (const key in limits) {
if (/^\w+Size$/.test(key)) {
limits[key] = bytes(limits[key]);
}
}
parseOptions.limits = Object.assign({}, parseOptions.limits, limits);
}
return parse(this, parseOptions);
},
æçäºco-busboyçparse使ç¨ï¼ææ¡£æ¨è使ç¨autoFields: true
我还看到一个问题,就是老版本的busboy在处理数据流的时候,会出现先 close 再 finish 的BUG
// node_modules/co-busboy/index.js
var Busboy = require('busboy')
这里的包依赖,我觉得试一下升级到最新的包,看一下数据流的处理。
切换到 node-v12.x 或者 node-v14.x 再试试,之前 co-busboy 有个 close 和 finish 先后时序问题导致的文件 pending:https://github.com/cojs/busboy/issues/41
cengzj@0201206761-NB MINGW64 /e/demo $ node -v v14.18.1 问题还是存在。 我现在的想法是改为使用 Stream 模式看一下。
@zenblofe 这两个上传文件 pending 的问题稍后我会看下的
我这边也遇到了一样的问题,然后也是调试到了co-busboy里面,确实是有问题。但是我测试了一下把co-busboy升级到1.5.0,问题貌似就没有了,好像是co-busboy修复了这个问题?我这边测试在node14和node16上,使用[email protected],文件上传都是ok的。这边建议egg-multipart把co-busboy的依赖版本调整到1.5.0以上应该就ok了。