app.js 3.2 KB

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