333
schangxiang@126.com
2025-09-19 18966e02fb573c7e2bb0c6426ed792b38b910940
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
'use strict';
 
const bytes = require('humanize-bytes');
const path = require('path');
 
module.exports = app => {
  const options = app.config.multipart;
  // make sure to cast the value of config **Size to number
  for (const key in options) {
    if (/^\w+Size$/.test(key)) {
      options[key] = bytes(options[key]);
    }
  }
 
  let checkExt;
  if (typeof options.whitelist === 'function') {
    checkExt = options.whitelist;
  } else if (Array.isArray(options.whitelist)) {
    options.whitelist = options.whitelist.map(extname => extname.toLowerCase());
    checkExt = filename => options.whitelist.includes(path.extname(filename).toLowerCase());
  } else {
    // default extname whitelist
    const whitelist = [
      // images
      '.jpg', '.jpeg', // image/jpeg
      '.png', // image/png, image/x-png
      '.gif', // image/gif
      '.bmp', // image/bmp
      '.wbmp', // image/vnd.wap.wbmp
      '.webp',
      '.tif',
      '.psd',
      // text
      '.svg',
      '.js', '.jsx',
      '.json',
      '.css', '.less',
      '.html', '.htm',
      '.xml',
      // tar
      '.zip',
      '.gz', '.tgz', '.gzip',
      // video
      '.mp3',
      '.mp4',
      '.avi',
    ]
      .concat(options.fileExtensions || [])
      .map(extname => extname.toLowerCase());
 
    checkExt = filename => whitelist.includes(path.extname(filename).toLowerCase());
  }
 
  // https://github.com/mscdex/busboy#busboy-methods
  app.config.multipartParseOptions = {
    autoFields: options.autoFields,
    defCharset: options.defaultCharset,
    limits: {
      fieldNameSize: options.fieldNameSize,
      fieldSize: options.fieldSize,
      fields: options.fields,
      fileSize: options.fileSize,
      files: options.files,
    },
    // check if extname in the whitelist
    checkFile(fieldname, fileStream, filename) {
      // just ignore, if no file
      if (!fileStream || !filename) return null;
 
      try {
        if (!checkExt(filename)) {
          const err = new Error('Invalid filename: ' + filename);
          err.status = 400;
          return err;
        }
      } catch (err) {
        err.status = 400;
        return err;
      }
    },
  };
 
  options.mode = options.mode || 'stream';
  if (![ 'stream', 'file' ].includes(options.mode)) {
    throw new TypeError(`Expect mode to be 'stream' or 'file', but got '${options.mode}'`);
  }
 
  app.coreLogger.info('[egg-multipart] %s mode enable', options.mode);
  if (options.mode === 'file') {
    app.coreLogger.info('[egg-multipart] will save temporary files to %j, cleanup job cron: %j',
      options.tmpdir, options.cleanSchedule.cron);
    // enable multipart middleware
    app.config.coreMiddleware.push('multipart');
  }
};