@midwayjs/upload 3.10.16 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,24 @@
1
1
  export declare const uploadWhiteList: string[];
2
+ export declare const DefaultUploadFileMimeType: {
3
+ '.jpg': string;
4
+ '.jpeg': string;
5
+ '.png': string;
6
+ '.gif': string;
7
+ '.bmp': string;
8
+ '.wbmp': string;
9
+ '.webp': string;
10
+ '.tif': string;
11
+ '.tiff': string;
12
+ '.psd': string;
13
+ '.svg': string;
14
+ '.xml': string;
15
+ '.pdf': string;
16
+ '.zip': string;
17
+ '.gz': string;
18
+ '.gzip': string;
19
+ '.mp3': string;
20
+ '.mp4': string;
21
+ '.avi': string;
22
+ };
23
+ export declare const EXT_KEY: unique symbol;
2
24
  //# sourceMappingURL=constants.d.ts.map
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.uploadWhiteList = void 0;
3
+ exports.EXT_KEY = exports.DefaultUploadFileMimeType = exports.uploadWhiteList = void 0;
4
4
  exports.uploadWhiteList = [
5
5
  // images
6
6
  '.jpg',
@@ -11,6 +11,7 @@ exports.uploadWhiteList = [
11
11
  '.wbmp',
12
12
  '.webp',
13
13
  '.tif',
14
+ '.tiff',
14
15
  '.psd',
15
16
  // text
16
17
  '.svg',
@@ -33,4 +34,27 @@ exports.uploadWhiteList = [
33
34
  '.mp4',
34
35
  '.avi',
35
36
  ];
37
+ // https://mimetype.io/
38
+ exports.DefaultUploadFileMimeType = {
39
+ '.jpg': 'image/jpeg',
40
+ '.jpeg': 'image/jpeg',
41
+ '.png': 'image/png',
42
+ '.gif': 'image/gif',
43
+ '.bmp': 'image/bmp',
44
+ '.wbmp': 'image/vnd.wap.wbmp',
45
+ '.webp': 'image/webp',
46
+ '.tif': 'image/tiff',
47
+ '.tiff': 'image/tiff',
48
+ '.psd': 'image/vnd.adobe.photoshop',
49
+ '.svg': 'image/svg+xml',
50
+ '.xml': 'application/xml',
51
+ '.pdf': 'application/pdf',
52
+ '.zip': 'application/zip',
53
+ '.gz': 'application/gzip',
54
+ '.gzip': 'application/gzip',
55
+ '.mp3': 'audio/mpeg',
56
+ '.mp4': 'video/mp4',
57
+ '.avi': 'video/x-msvideo',
58
+ };
59
+ exports.EXT_KEY = Symbol('_ext');
36
60
  //# sourceMappingURL=constants.js.map
package/dist/error.d.ts CHANGED
@@ -2,4 +2,7 @@ import { httpError } from '@midwayjs/core';
2
2
  export declare class MultipartInvalidFilenameError extends httpError.BadRequestError {
3
3
  constructor(filename: string);
4
4
  }
5
+ export declare class MultipartInvalidFileTypeError extends httpError.BadRequestError {
6
+ constructor(filename: string, currentType: string, type: string);
7
+ }
5
8
  //# sourceMappingURL=error.d.ts.map
package/dist/error.js CHANGED
@@ -1,11 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MultipartInvalidFilenameError = void 0;
3
+ exports.MultipartInvalidFileTypeError = exports.MultipartInvalidFilenameError = void 0;
4
4
  const core_1 = require("@midwayjs/core");
5
5
  class MultipartInvalidFilenameError extends core_1.httpError.BadRequestError {
6
6
  constructor(filename) {
7
- super(`Invalid update file name ${filename}, please check it`);
7
+ super(`Invalid upload file name ${filename}, please check it`);
8
8
  }
9
9
  }
10
10
  exports.MultipartInvalidFilenameError = MultipartInvalidFilenameError;
11
+ class MultipartInvalidFileTypeError extends core_1.httpError.BadRequestError {
12
+ constructor(filename, currentType, type) {
13
+ super(`Invalid upload file type, ${filename} type(${currentType || 'unknown'}) is not ${type} , please check it`);
14
+ }
15
+ }
16
+ exports.MultipartInvalidFileTypeError = MultipartInvalidFileTypeError;
11
17
  //# sourceMappingURL=error.js.map
@@ -1,13 +1,44 @@
1
1
  /// <reference types="node" />
2
- import { Readable } from "stream";
2
+ import { Readable } from 'stream';
3
+ import { IgnoreMatcher } from '@midwayjs/core';
3
4
  export type UploadMode = 'stream' | 'file';
4
5
  export interface UploadOptions {
6
+ /**
7
+ * Upload mode, default is `file`
8
+ */
5
9
  mode?: UploadMode;
10
+ /**
11
+ * Max file size (in bytes), default is `10mb`
12
+ */
6
13
  fileSize?: string;
14
+ /**
15
+ * The white ext file names
16
+ */
7
17
  whitelist?: string[] | null;
18
+ /**
19
+ * Temporary file directory
20
+ */
8
21
  tmpdir?: string;
22
+ /**
23
+ * Temporary file automatic cleanup time, default is 5 minutes
24
+ */
9
25
  cleanTimeout?: number;
26
+ /**
27
+ * Whether the uploaded body is base64, for example, apigw of Tencent Cloud
28
+ */
10
29
  base64?: boolean;
30
+ /**
31
+ * Which paths to ignore
32
+ */
33
+ ignore?: IgnoreMatcher<any> | IgnoreMatcher<any>[];
34
+ /**
35
+ * Match those paths with higher priority than ignore
36
+ */
37
+ match?: IgnoreMatcher<any> | IgnoreMatcher<any>[];
38
+ /**
39
+ * Mime type white list
40
+ */
41
+ mimeTypeWhiteList?: Record<string, string | string[]>;
11
42
  }
12
43
  export interface UploadFileInfo<T> {
13
44
  filename: string;
@@ -1,14 +1,24 @@
1
- import { IMiddleware, IMidwayLogger } from '@midwayjs/core';
1
+ /// <reference types="node" />
2
+ import { IMiddleware, IMidwayLogger, IgnoreMatcher } from '@midwayjs/core';
2
3
  import { UploadOptions } from '.';
3
4
  export declare class UploadMiddleware implements IMiddleware<any, any> {
4
- upload: UploadOptions;
5
+ uploadConfig: UploadOptions;
5
6
  logger: IMidwayLogger;
6
7
  private uploadWhiteListMap;
8
+ private uploadFileMimeTypeMap;
9
+ match: IgnoreMatcher<any>[];
10
+ ignore: IgnoreMatcher<any>[];
11
+ init(): Promise<void>;
7
12
  resolve(app: any): (req: any, res: any, next: any) => Promise<any>;
8
13
  execUpload(ctx: any, req: any, res: any, next: any, isExpress: any): Promise<any>;
9
14
  getUploadBoundary(request: any): false | string;
10
15
  isReadableStream(req: any, isExpress: any): boolean;
11
- checkExt(filename: any): string | boolean;
16
+ checkAndGetExt(filename: any): string | boolean;
17
+ checkFileType(ext: string, data: Buffer): Promise<{
18
+ passed: boolean;
19
+ mime?: string;
20
+ current?: string;
21
+ }>;
12
22
  static getName(): string;
13
23
  }
14
24
  //# sourceMappingURL=middleware.d.ts.map
@@ -17,15 +17,32 @@ const stream_1 = require("stream");
17
17
  const _1 = require(".");
18
18
  const parse_1 = require("./parse");
19
19
  const getRawBody = require("raw-body");
20
+ const file_type_1 = require("file-type");
21
+ const utils_1 = require("./utils");
20
22
  const { unlink, writeFile } = fs_1.promises;
21
23
  let UploadMiddleware = class UploadMiddleware {
22
24
  constructor() {
23
- this.uploadWhiteListMap = {};
25
+ this.uploadWhiteListMap = new Map();
26
+ this.uploadFileMimeTypeMap = new Map();
27
+ }
28
+ async init() {
29
+ if (this.uploadConfig.match) {
30
+ this.match = [].concat(this.uploadConfig.match || []);
31
+ }
32
+ else {
33
+ this.ignore = [].concat(this.uploadConfig.ignore || []);
34
+ }
24
35
  }
25
36
  resolve(app) {
26
- if (Array.isArray(this.upload.whitelist)) {
27
- for (const whiteExt of this.upload.whitelist) {
28
- this.uploadWhiteListMap[whiteExt] = true;
37
+ if (Array.isArray(this.uploadConfig.whitelist)) {
38
+ for (const whiteExt of this.uploadConfig.whitelist) {
39
+ this.uploadWhiteListMap.set(whiteExt, whiteExt);
40
+ }
41
+ }
42
+ if (this.uploadConfig.mimeTypeWhiteList) {
43
+ for (const ext in this.uploadConfig.mimeTypeWhiteList) {
44
+ const mime = [].concat(this.uploadConfig.mimeTypeWhiteList[ext]);
45
+ this.uploadFileMimeTypeMap.set(ext, mime);
29
46
  }
30
47
  }
31
48
  if (app.getFrameworkType() === core_1.MidwayFrameworkType.WEB_EXPRESS) {
@@ -43,7 +60,7 @@ let UploadMiddleware = class UploadMiddleware {
43
60
  }
44
61
  async execUpload(ctx, req, res, next, isExpress) {
45
62
  var _a;
46
- const { mode, tmpdir, fileSize } = this.upload;
63
+ const { mode, tmpdir, fileSize } = this.uploadConfig;
47
64
  const boundary = this.getUploadBoundary(req);
48
65
  if (!boundary) {
49
66
  return next();
@@ -72,12 +89,12 @@ let UploadMiddleware = class UploadMiddleware {
72
89
  if (this.isReadableStream(req, isExpress)) {
73
90
  if (mode === 'stream') {
74
91
  const { fields, fileInfo } = await (0, parse_1.parseFromReadableStream)(req, boundary);
75
- const ext = this.checkExt(fileInfo.filename);
92
+ const ext = this.checkAndGetExt(fileInfo.filename);
76
93
  if (!ext) {
77
94
  throw new _1.MultipartInvalidFilenameError(fileInfo.filename);
78
95
  }
79
96
  else {
80
- fileInfo['_ext'] = ext;
97
+ fileInfo[_1.EXT_KEY] = ext;
81
98
  ctx.fields = fields;
82
99
  ctx.files = [fileInfo];
83
100
  return next();
@@ -95,27 +112,28 @@ let UploadMiddleware = class UploadMiddleware {
95
112
  else {
96
113
  body = req.body;
97
114
  }
98
- const data = await (0, parse_1.parseMultipart)(body, boundary, this.upload);
115
+ const data = await (0, parse_1.parseMultipart)(body, boundary, this.uploadConfig);
99
116
  if (!data) {
100
117
  return next();
101
118
  }
102
119
  ctx.fields = data.fields;
103
120
  const requireId = `upload_${Date.now()}.${Math.random()}`;
104
121
  const files = data.files;
105
- const notCheckFile = files.find(fileInfo => {
106
- const ext = this.checkExt(fileInfo.filename);
122
+ for (const fileInfo of files) {
123
+ const ext = this.checkAndGetExt(fileInfo.filename);
107
124
  if (!ext) {
108
- return fileInfo;
125
+ throw new _1.MultipartInvalidFilenameError(fileInfo.filename);
109
126
  }
110
- fileInfo['_ext'] = ext;
111
- });
112
- if (notCheckFile) {
113
- throw new _1.MultipartInvalidFilenameError(notCheckFile.filename);
127
+ const { passed, mime, current } = await this.checkFileType(ext, fileInfo.data);
128
+ if (!passed) {
129
+ throw new _1.MultipartInvalidFileTypeError(fileInfo.filename, current, mime);
130
+ }
131
+ fileInfo[_1.EXT_KEY] = ext;
114
132
  }
115
133
  ctx.files = await Promise.all(files.map(async (file, index) => {
116
- const { data, filename } = file;
134
+ const { data } = file;
117
135
  if (mode === 'file') {
118
- const ext = file['_ext'] || (0, path_1.extname)(filename);
136
+ const ext = file[_1.EXT_KEY];
119
137
  const tmpFileName = (0, path_1.resolve)(tmpdir, `${requireId}.${index}${ext}`);
120
138
  await writeFile(tmpFileName, data, 'binary');
121
139
  file.data = tmpFileName;
@@ -167,17 +185,46 @@ let UploadMiddleware = class UploadMiddleware {
167
185
  }
168
186
  return false;
169
187
  }
170
- checkExt(filename) {
188
+ // check extentions
189
+ checkAndGetExt(filename) {
171
190
  const lowerCaseFileNameList = filename.toLowerCase().split('.');
172
191
  while (lowerCaseFileNameList.length) {
173
192
  lowerCaseFileNameList.shift();
174
193
  const curExt = `.${lowerCaseFileNameList.join('.')}`;
175
- if (this.upload.whitelist === null || this.uploadWhiteListMap[curExt]) {
176
- return curExt;
194
+ if (this.uploadConfig.whitelist === null) {
195
+ return (0, utils_1.formatExt)(curExt);
196
+ }
197
+ if (this.uploadWhiteListMap.has(curExt)) {
198
+ // Avoid the presence of hidden characters and return extensions in the white list.
199
+ return this.uploadWhiteListMap.get(curExt);
177
200
  }
178
201
  }
179
202
  return false;
180
203
  }
204
+ // check file-type
205
+ async checkFileType(ext, data) {
206
+ // fileType == null, pass check
207
+ if (!this.uploadConfig.mimeTypeWhiteList) {
208
+ return { passed: true };
209
+ }
210
+ const mime = this.uploadFileMimeTypeMap.get(ext);
211
+ if (!mime) {
212
+ return { passed: false, mime: ext };
213
+ }
214
+ if (!mime.length) {
215
+ return { passed: true };
216
+ }
217
+ const typeInfo = await (0, file_type_1.fromBuffer)(data);
218
+ if (!typeInfo) {
219
+ return { passed: false, mime: mime.join('、') };
220
+ }
221
+ const findMime = mime.find(mimeItem => mimeItem === typeInfo.mime);
222
+ return {
223
+ passed: !!findMime,
224
+ mime: mime.join('、'),
225
+ current: typeInfo.mime,
226
+ };
227
+ }
181
228
  static getName() {
182
229
  return 'upload';
183
230
  }
@@ -185,11 +232,17 @@ let UploadMiddleware = class UploadMiddleware {
185
232
  __decorate([
186
233
  (0, core_1.Config)('upload'),
187
234
  __metadata("design:type", Object)
188
- ], UploadMiddleware.prototype, "upload", void 0);
235
+ ], UploadMiddleware.prototype, "uploadConfig", void 0);
189
236
  __decorate([
190
237
  (0, core_1.Logger)(),
191
238
  __metadata("design:type", Object)
192
239
  ], UploadMiddleware.prototype, "logger", void 0);
240
+ __decorate([
241
+ (0, core_1.Init)(),
242
+ __metadata("design:type", Function),
243
+ __metadata("design:paramtypes", []),
244
+ __metadata("design:returntype", Promise)
245
+ ], UploadMiddleware.prototype, "init", null);
193
246
  UploadMiddleware = __decorate([
194
247
  (0, core_1.Middleware)()
195
248
  ], UploadMiddleware);
package/dist/utils.d.ts CHANGED
@@ -2,4 +2,5 @@ export declare const autoRemoveUploadTmpFile: (tmpDir: string, cleanTimeout: num
2
2
  export declare const stopAutoRemoveUploadTmpFile: () => Promise<void>;
3
3
  export declare const checkExists: (path: string) => Promise<boolean>;
4
4
  export declare const ensureDir: (dirPath: string) => Promise<boolean>;
5
+ export declare const formatExt: (ext: string) => string;
5
6
  //# sourceMappingURL=utils.d.ts.map
package/dist/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ensureDir = exports.checkExists = exports.stopAutoRemoveUploadTmpFile = exports.autoRemoveUploadTmpFile = void 0;
3
+ exports.formatExt = exports.ensureDir = exports.checkExists = exports.stopAutoRemoveUploadTmpFile = exports.autoRemoveUploadTmpFile = void 0;
4
4
  const fs_1 = require("fs");
5
5
  const path_1 = require("path");
6
6
  const { readdir, access, stat, unlink, mkdir } = fs_1.promises;
@@ -68,4 +68,24 @@ const ensureDir = async (dirPath) => {
68
68
  }
69
69
  };
70
70
  exports.ensureDir = ensureDir;
71
+ const formatExt = (ext) => {
72
+ return Buffer.from(ext.toLowerCase())
73
+ .filter(ext => {
74
+ // .
75
+ if (ext === 0x2e) {
76
+ return true;
77
+ }
78
+ // 0-9
79
+ if (ext >= 0x30 && ext <= 0x39) {
80
+ return true;
81
+ }
82
+ // a-z
83
+ if (ext >= 0x61 && ext <= 0x7a) {
84
+ return true;
85
+ }
86
+ return false;
87
+ })
88
+ .toString();
89
+ };
90
+ exports.formatExt = formatExt;
71
91
  //# sourceMappingURL=utils.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midwayjs/upload",
3
- "version": "3.10.16",
3
+ "version": "3.11.0",
4
4
  "description": "Midway Component for upload",
5
5
  "main": "dist/index.js",
6
6
  "typings": "index.d.ts",
@@ -22,16 +22,16 @@
22
22
  },
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
+ "file-type": "16.5.4",
25
26
  "raw-body": "2.5.2"
26
27
  },
27
28
  "devDependencies": {
28
- "@midwayjs/core": "^3.10.15",
29
- "@midwayjs/express": "^3.10.15",
30
- "@midwayjs/faas": "^3.10.15",
31
- "@midwayjs/koa": "^3.10.15",
32
- "@midwayjs/mock": "^3.10.15",
33
- "@midwayjs/serverless-app": "^3.10.15",
34
- "@midwayjs/web": "^3.10.16"
29
+ "@midwayjs/core": "^3.11.0",
30
+ "@midwayjs/express": "^3.11.0",
31
+ "@midwayjs/faas": "^3.11.0",
32
+ "@midwayjs/koa": "^3.11.0",
33
+ "@midwayjs/mock": "^3.11.0",
34
+ "@midwayjs/web": "^3.11.0"
35
35
  },
36
- "gitHead": "37bfe5eecf2500a798c0bebfcf59c540ce9479e1"
36
+ "gitHead": "eadb977e7fddcd4287c099fc32b601cd51702514"
37
37
  }