eggjs / egg

🥚 Born to build better enterprise frameworks and apps with Node.js & Koa

Home Page:https://eggjs.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Reach filesize limit,egg-multipart上传失败以后,临时文件无法删除,会导致服务器占用存储空间越来越大

litteredxiao opened this issue · comments

在此输入你需要反馈的 Bug 具体信息(Bug in Detail):

通过egg-multipart上传的文件超过默认配置的大小限制,会抛出异常,但是无法删除上传失败的临时文件,上传的临时文件就是egg-mulipart配置的filesize大小,最终会导致服务器存储空间占满

上传文件抛出异常以后,catch中捕获的异常requestFiles是空数组

可复现问题的仓库地址(Reproduction Repo)

async saveRequestFiles(options = {}) {
const ctx = this;

const allowArrayField = ctx.app.config.multipart.allowArrayField;

let storedir;

const requestBody = {};
const requestFiles = [];

options.autoFields = false;
const parts = ctx.multipart(options);

try {
  for await (const part of parts) {
    if (Array.isArray(part)) {
      // fields
      const [ fieldName, fieldValue ] = part;
      if (!allowArrayField) {
        requestBody[fieldName] = fieldValue;
      } else {
        if (!requestBody[fieldName]) {
          requestBody[fieldName] = fieldValue;
        } else if (!Array.isArray(requestBody[fieldName])) {
          requestBody[fieldName] = [ requestBody[fieldName], fieldValue ];
        } else {
          requestBody[fieldName].push(fieldValue);
        }
      }
    } else {
      // stream
      const { filename, fieldname, encoding, mime } = part;

      if (!storedir) {
        // ${tmpdir}/YYYY/MM/DD/HH
        storedir = path.join(ctx.app.config.multipart.tmpdir, dayjs().format('YYYY/MM/DD/HH'));
        await fs.mkdir(storedir, { recursive: true });
      }

      // write to tmp file
      const filepath = path.join(storedir, randomUUID() + path.extname(filename));
      const target = createWriteStream(filepath);
      await pipeline(part, target);

      const meta = {
        filepath,
        field: fieldname,
        filename,
        encoding,
        mime,
        // keep same property name as file stream, https://github.com/cojs/busboy/blob/master/index.js#L114
        fieldname,
        transferEncoding: encoding,
        mimeType: mime,
      };

      requestFiles.push(meta);
    }
  }
} catch (err) {
  await ctx.cleanupRequestFiles(requestFiles);
  throw err;
}

ctx.request.body = requestBody;
ctx.request.files = requestFiles;

},

Node 版本号:

v14.21.3

Eggjs 版本号:

3.3.3

相关插件名称与版本号(PlugIn and Name):

egg-multipart 3.3.0

操作平台与版本号(Platform and Version):

linux

https://www.eggjs.org/zh-CN/basics/controller#%E8%8E%B7%E5%8F%96%E4%B8%8A%E4%BC%A0%E7%9A%84%E6%96%87%E4%BB%B6 建议你改成 File 模式,Stream 模式需要做很多处理才能做好这件事情。
你上面贴的代码明显就是没有处理好各种异常边界条件,导致临时文件没有处理。

@fengmk2
上面贴的代码并非业务代码,而是egg-multipart插件中的源码
源码中的await pipeline(part, target);这一行代码报错,触发了catch以后无法删除临时文件,因为原本定义的requestFiles.push(meta);这行代码无法执行,requestFiles这个数组是空数组

我们原有的使用方式就是file模式