@midwayjs/busboy 3.17.1 → 3.18.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.
- package/dist/error.js +2 -2
- package/dist/interface.d.ts +19 -1
- package/dist/middleware.d.ts +6 -3
- package/dist/middleware.js +237 -152
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +7 -1
- package/package.json +8 -8
package/dist/error.js
CHANGED
|
@@ -4,13 +4,13 @@ exports.MultipartFieldsLimitError = exports.MultipartPartsLimitError = exports.M
|
|
|
4
4
|
const core_1 = require("@midwayjs/core");
|
|
5
5
|
class MultipartInvalidFilenameError extends core_1.httpError.BadRequestError {
|
|
6
6
|
constructor(filename) {
|
|
7
|
-
super(`Invalid
|
|
7
|
+
super(`Invalid file name or suffix "${filename}".`);
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
exports.MultipartInvalidFilenameError = MultipartInvalidFilenameError;
|
|
11
11
|
class MultipartInvalidFileTypeError extends core_1.httpError.BadRequestError {
|
|
12
12
|
constructor(filename, currentType, type) {
|
|
13
|
-
super(`Invalid
|
|
13
|
+
super(`Invalid file type, "${filename}" type(${currentType || 'unknown'}) is not ${type}.`);
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
exports.MultipartInvalidFileTypeError = MultipartInvalidFileTypeError;
|
package/dist/interface.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Readable } from 'stream';
|
|
3
3
|
import { IgnoreMatcher, IMidwayContext } from '@midwayjs/core';
|
|
4
4
|
import { BusboyConfig } from 'busboy';
|
|
5
|
-
export type UploadMode = 'stream' | 'file';
|
|
5
|
+
export type UploadMode = 'stream' | 'file' | 'asyncIterator';
|
|
6
6
|
export interface UploadOptions extends BusboyConfig {
|
|
7
7
|
/**
|
|
8
8
|
* Upload mode, default is `file`
|
|
@@ -50,6 +50,10 @@ export interface UploadFileInfo {
|
|
|
50
50
|
* file data, a string of path
|
|
51
51
|
*/
|
|
52
52
|
data: string;
|
|
53
|
+
/**
|
|
54
|
+
* field name
|
|
55
|
+
*/
|
|
56
|
+
fieldName: string;
|
|
53
57
|
}
|
|
54
58
|
export interface UploadStreamFileInfo {
|
|
55
59
|
/**
|
|
@@ -64,5 +68,19 @@ export interface UploadStreamFileInfo {
|
|
|
64
68
|
* file data, Readable stream
|
|
65
69
|
*/
|
|
66
70
|
data: Readable;
|
|
71
|
+
/**
|
|
72
|
+
* field name
|
|
73
|
+
*/
|
|
74
|
+
fieldName: string;
|
|
75
|
+
}
|
|
76
|
+
export interface UploadStreamFieldInfo {
|
|
77
|
+
/**
|
|
78
|
+
* field name
|
|
79
|
+
*/
|
|
80
|
+
name: string;
|
|
81
|
+
/**
|
|
82
|
+
* field value
|
|
83
|
+
*/
|
|
84
|
+
value: any;
|
|
67
85
|
}
|
|
68
86
|
//# sourceMappingURL=interface.d.ts.map
|
package/dist/middleware.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { IMiddleware, ILogger, IgnoreMatcher, IMidwayApplication } from '@midwayjs/core';
|
|
3
|
-
import { UploadOptions } from '
|
|
3
|
+
import { UploadOptions, UploadMode } from './interface';
|
|
4
4
|
import { BusboyConfig } from 'busboy';
|
|
5
5
|
export declare class UploadMiddleware implements IMiddleware<any, any> {
|
|
6
6
|
uploadConfig: UploadOptions;
|
|
@@ -17,10 +17,13 @@ export declare class UploadMiddleware implements IMiddleware<any, any> {
|
|
|
17
17
|
private uploadFileMimeTypeMap;
|
|
18
18
|
match: IgnoreMatcher<any>[];
|
|
19
19
|
ignore: IgnoreMatcher<any>[];
|
|
20
|
-
init(): Promise<void>;
|
|
20
|
+
protected init(): Promise<void>;
|
|
21
21
|
resolve(app: IMidwayApplication, options?: {
|
|
22
|
-
mode?:
|
|
22
|
+
mode?: UploadMode;
|
|
23
23
|
} & BusboyConfig): (ctxOrReq: any, resOrNext: any, next: any) => Promise<any>;
|
|
24
|
+
private processFileOrStream;
|
|
25
|
+
private processAsyncIterator;
|
|
26
|
+
private processServerlessUpload;
|
|
24
27
|
static getName(): string;
|
|
25
28
|
checkAndGetExt(filename: any, whiteListMap?: Map<string, string>): string | boolean;
|
|
26
29
|
checkFileType(ext: string, data: Buffer, uploadFileMimeTypeMap?: Map<string, string[]>): Promise<{
|
package/dist/middleware.js
CHANGED
|
@@ -14,11 +14,12 @@ const core_1 = require("@midwayjs/core");
|
|
|
14
14
|
const path_1 = require("path");
|
|
15
15
|
const fs_1 = require("fs");
|
|
16
16
|
const stream_1 = require("stream");
|
|
17
|
-
const
|
|
17
|
+
const error_1 = require("./error");
|
|
18
18
|
const parse_1 = require("./parse");
|
|
19
19
|
const file_type_1 = require("file-type");
|
|
20
20
|
const utils_1 = require("./utils");
|
|
21
21
|
const busboy = require("busboy");
|
|
22
|
+
const constants_1 = require("./constants");
|
|
22
23
|
const { unlink, writeFile } = fs_1.promises;
|
|
23
24
|
let UploadMiddleware = class UploadMiddleware {
|
|
24
25
|
constructor() {
|
|
@@ -59,14 +60,14 @@ let UploadMiddleware = class UploadMiddleware {
|
|
|
59
60
|
? (0, core_1.extend)({}, this.uploadConfig, options || {})
|
|
60
61
|
: this.uploadConfig;
|
|
61
62
|
return async (ctxOrReq, resOrNext, next) => {
|
|
62
|
-
var _a
|
|
63
|
+
var _a;
|
|
63
64
|
const req = ((_a = ctxOrReq.request) === null || _a === void 0 ? void 0 : _a.req) || ctxOrReq.request || ctxOrReq;
|
|
64
65
|
next = isExpress ? next : resOrNext;
|
|
65
66
|
const boundary = this.getUploadBoundary(req);
|
|
66
67
|
if (!boundary) {
|
|
67
68
|
return next();
|
|
68
69
|
}
|
|
69
|
-
const
|
|
70
|
+
const useAsyncIterator = uploadConfig.mode === 'asyncIterator';
|
|
70
71
|
// create new map include custom white list
|
|
71
72
|
const currentContextWhiteListMap = new Map([
|
|
72
73
|
...this.uploadWhiteListMap.entries(),
|
|
@@ -105,166 +106,250 @@ let UploadMiddleware = class UploadMiddleware {
|
|
|
105
106
|
}));
|
|
106
107
|
};
|
|
107
108
|
if (this.isReadableStream(req, isExpress)) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
109
|
+
if (useAsyncIterator) {
|
|
110
|
+
await this.processAsyncIterator(req, uploadConfig, currentContextWhiteListMap, currentContextMimeTypeWhiteListMap, ctxOrReq);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
await this.processFileOrStream(req, uploadConfig, currentContextWhiteListMap, currentContextMimeTypeWhiteListMap, ctxOrReq);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
await this.processServerlessUpload(req, boundary, uploadConfig, next, currentContextWhiteListMap, currentContextMimeTypeWhiteListMap, ctxOrReq);
|
|
118
|
+
}
|
|
119
|
+
await next();
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
async processFileOrStream(req, uploadConfig, currentContextWhiteListMap, currentContextMimeTypeWhiteListMap, ctxOrReq) {
|
|
123
|
+
let isStreamResolve = false;
|
|
124
|
+
const { mode, tmpdir } = uploadConfig;
|
|
125
|
+
const { files = [], fields = [] } = await new Promise((resolveP, reject) => {
|
|
126
|
+
const bb = busboy({
|
|
127
|
+
headers: req.headers,
|
|
128
|
+
...uploadConfig,
|
|
129
|
+
});
|
|
130
|
+
const fields = [];
|
|
131
|
+
const files = [];
|
|
132
|
+
bb.on('file', async (name, file, info) => {
|
|
133
|
+
this.logger.debug('[busboy]: busboy file event');
|
|
134
|
+
const { filename, encoding, mimeType } = info;
|
|
135
|
+
const ext = this.checkAndGetExt(filename, currentContextWhiteListMap);
|
|
136
|
+
if (!ext) {
|
|
137
|
+
reject(new error_1.MultipartInvalidFilenameError(filename));
|
|
138
|
+
}
|
|
139
|
+
file.once('limit', () => {
|
|
140
|
+
reject(new error_1.MultipartFileSizeLimitError(filename));
|
|
141
|
+
});
|
|
142
|
+
if (mode === 'stream') {
|
|
143
|
+
if (isStreamResolve) {
|
|
144
|
+
// will be skip
|
|
145
|
+
file.resume();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
files.push(new Promise(resolve => {
|
|
149
|
+
resolve({
|
|
150
|
+
fieldName: name,
|
|
151
|
+
filename,
|
|
152
|
+
encoding,
|
|
153
|
+
mimeType,
|
|
154
|
+
data: file,
|
|
125
155
|
});
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
else {
|
|
147
|
-
fileModeCount++;
|
|
148
|
-
// file mode
|
|
149
|
-
const requireId = `upload_${Date.now()}.${Math.random()}`;
|
|
150
|
-
// read stream pipe to temp file
|
|
151
|
-
const tempFile = (0, path_1.resolve)(tmpdir, `${requireId}${ext}`);
|
|
152
|
-
// get buffer from stream, and check file type
|
|
153
|
-
file.once('data', async (chunk) => {
|
|
154
|
-
const { passed, mime, current } = await this.checkFileType(ext, chunk, currentContextMimeTypeWhiteListMap);
|
|
155
|
-
if (!passed) {
|
|
156
|
-
file.pause();
|
|
157
|
-
reject(new _1.MultipartInvalidFileTypeError(filename, current, mime));
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
const writeStream = file.pipe((0, fs_1.createWriteStream)(tempFile));
|
|
161
|
-
file.on('end', () => {
|
|
162
|
-
fileModeCount--;
|
|
163
|
-
});
|
|
164
|
-
writeStream.on('error', reject);
|
|
165
|
-
writeStream.on('finish', async () => {
|
|
166
|
-
files.push(new Promise(resolve => {
|
|
167
|
-
resolve({
|
|
168
|
-
filename,
|
|
169
|
-
mimeType,
|
|
170
|
-
encoding,
|
|
171
|
-
data: tempFile,
|
|
172
|
-
});
|
|
173
|
-
}));
|
|
174
|
-
if (fileModeCount === 0) {
|
|
175
|
-
return resolveP({
|
|
176
|
-
fields: await Promise.all(fields),
|
|
177
|
-
files: await Promise.all(files),
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
});
|
|
156
|
+
}));
|
|
157
|
+
isStreamResolve = true;
|
|
158
|
+
// busboy 这里无法触发 close 事件,所以这里直接 resolve
|
|
159
|
+
return resolveP({
|
|
160
|
+
fields,
|
|
161
|
+
files: await Promise.all(files),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
this.logger.debug('[busboy]: file mode, file data event');
|
|
166
|
+
// file mode
|
|
167
|
+
const requireId = `upload_${Date.now()}.${Math.random()}`;
|
|
168
|
+
// read stream pipe to temp file
|
|
169
|
+
const tempFile = (0, path_1.resolve)(tmpdir, `${requireId}${ext}`);
|
|
170
|
+
// get buffer from stream, and check file type
|
|
171
|
+
file.once('data', async (chunk) => {
|
|
172
|
+
const { passed, mime, current } = await this.checkFileType(ext, chunk, currentContextMimeTypeWhiteListMap);
|
|
173
|
+
if (!passed) {
|
|
174
|
+
file.pause();
|
|
175
|
+
reject(new error_1.MultipartInvalidFileTypeError(filename, current, mime));
|
|
181
176
|
}
|
|
182
177
|
});
|
|
183
|
-
|
|
184
|
-
|
|
178
|
+
const fp = new Promise(resolve => {
|
|
179
|
+
const writeStream = file.pipe((0, fs_1.createWriteStream)(tempFile));
|
|
180
|
+
writeStream.on('error', reject);
|
|
181
|
+
writeStream.on('finish', () => {
|
|
185
182
|
resolve({
|
|
186
|
-
|
|
187
|
-
|
|
183
|
+
filename,
|
|
184
|
+
mimeType,
|
|
185
|
+
encoding,
|
|
186
|
+
fieldName: name,
|
|
187
|
+
data: tempFile,
|
|
188
188
|
});
|
|
189
|
-
})
|
|
190
|
-
});
|
|
191
|
-
bb.on('error', (err) => {
|
|
192
|
-
reject(new _1.MultipartError(err));
|
|
193
|
-
});
|
|
194
|
-
bb.on('partsLimit', () => {
|
|
195
|
-
reject(new _1.MultipartPartsLimitError());
|
|
196
|
-
});
|
|
197
|
-
bb.on('filesLimit', () => {
|
|
198
|
-
reject(new _1.MultipartFileLimitError());
|
|
199
|
-
});
|
|
200
|
-
bb.on('fieldsLimit', () => {
|
|
201
|
-
reject(new _1.MultipartFieldsLimitError());
|
|
189
|
+
});
|
|
202
190
|
});
|
|
203
|
-
|
|
191
|
+
files.push(fp);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
bb.on('close', async () => {
|
|
195
|
+
this.logger.debug('[busboy]: busboy close');
|
|
196
|
+
// close 事件会在 busboy 解析完所有数据后触发,但是这个时候有可能没有写完文件,所以使用 Promise.all 等待所有文件写入完成
|
|
197
|
+
resolveP({
|
|
198
|
+
fields,
|
|
199
|
+
files: await Promise.all(files),
|
|
204
200
|
});
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
get() {
|
|
212
|
-
return ctxOrReq.files[0];
|
|
213
|
-
},
|
|
214
|
-
set(v) {
|
|
215
|
-
ctxOrReq.files = [v];
|
|
216
|
-
},
|
|
201
|
+
});
|
|
202
|
+
bb.on('field', (name, value, info) => {
|
|
203
|
+
fields.push({
|
|
204
|
+
name,
|
|
205
|
+
value,
|
|
206
|
+
info,
|
|
217
207
|
});
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
208
|
+
});
|
|
209
|
+
bb.on('error', (err) => {
|
|
210
|
+
reject(new error_1.MultipartError(err));
|
|
211
|
+
});
|
|
212
|
+
bb.on('partsLimit', () => {
|
|
213
|
+
reject(new error_1.MultipartPartsLimitError());
|
|
214
|
+
});
|
|
215
|
+
bb.on('filesLimit', () => {
|
|
216
|
+
reject(new error_1.MultipartFileLimitError());
|
|
217
|
+
});
|
|
218
|
+
bb.on('fieldsLimit', () => {
|
|
219
|
+
reject(new error_1.MultipartFieldsLimitError());
|
|
220
|
+
});
|
|
221
|
+
req.pipe(bb);
|
|
222
|
+
});
|
|
223
|
+
ctxOrReq.files = files;
|
|
224
|
+
ctxOrReq.fields = fields.reduce((accumulator, current) => {
|
|
225
|
+
accumulator[current.name] = current.value;
|
|
226
|
+
return accumulator;
|
|
227
|
+
}, {});
|
|
228
|
+
}
|
|
229
|
+
async processAsyncIterator(req, uploadConfig, currentContextWhiteListMap, currentContextMimeTypeWhiteListMap, ctxOrReq) {
|
|
230
|
+
const bb = busboy({
|
|
231
|
+
headers: req.headers,
|
|
232
|
+
...uploadConfig,
|
|
233
|
+
});
|
|
234
|
+
const fileReadable = new stream_1.Readable({
|
|
235
|
+
objectMode: true,
|
|
236
|
+
read() { },
|
|
237
|
+
autoDestroy: true,
|
|
238
|
+
});
|
|
239
|
+
const fieldReadable = new stream_1.Readable({
|
|
240
|
+
objectMode: true,
|
|
241
|
+
read() { },
|
|
242
|
+
autoDestroy: true,
|
|
243
|
+
});
|
|
244
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
245
|
+
const self = this;
|
|
246
|
+
async function* streamToAsyncIteratorWithError(stream) {
|
|
247
|
+
for await (const chunk of stream) {
|
|
248
|
+
chunk.data.once('data', async (chunk) => {
|
|
249
|
+
const { passed, mime, current } = await self.checkFileType(chunk.ext, chunk, currentContextMimeTypeWhiteListMap);
|
|
242
250
|
if (!passed) {
|
|
243
|
-
throw new
|
|
251
|
+
throw new error_1.MultipartInvalidFileTypeError(chunk.filename, current, mime);
|
|
244
252
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
ctxOrReq.files = await Promise.all(files.map(async (file, index) => {
|
|
248
|
-
const { data } = file;
|
|
249
|
-
if (mode === 'file') {
|
|
250
|
-
const ext = file[_1.EXT_KEY];
|
|
251
|
-
const tmpFileName = (0, path_1.resolve)(tmpdir, `${requireId}.${index}${ext}`);
|
|
252
|
-
await writeFile(tmpFileName, data, 'binary');
|
|
253
|
-
file.data = tmpFileName;
|
|
254
|
-
}
|
|
255
|
-
else if (mode === 'stream') {
|
|
256
|
-
file.data = new stream_1.Readable({
|
|
257
|
-
read() {
|
|
258
|
-
this.push(data);
|
|
259
|
-
this.push(null);
|
|
260
|
-
},
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
return file;
|
|
264
|
-
}));
|
|
253
|
+
});
|
|
254
|
+
yield chunk;
|
|
265
255
|
}
|
|
266
|
-
|
|
267
|
-
|
|
256
|
+
}
|
|
257
|
+
const files = streamToAsyncIteratorWithError(fileReadable);
|
|
258
|
+
const fields = (0, utils_1.streamToAsyncIterator)(fieldReadable);
|
|
259
|
+
bb.on('file', (name, file, info) => {
|
|
260
|
+
const { filename, encoding, mimeType } = info;
|
|
261
|
+
const ext = this.checkAndGetExt(filename, currentContextWhiteListMap);
|
|
262
|
+
if (!ext) {
|
|
263
|
+
fileReadable.destroy(new error_1.MultipartInvalidFilenameError(filename));
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
file.once('limit', () => {
|
|
267
|
+
fileReadable.destroy(new error_1.MultipartFileSizeLimitError(filename));
|
|
268
|
+
});
|
|
269
|
+
fileReadable.push({
|
|
270
|
+
fieldName: name,
|
|
271
|
+
filename,
|
|
272
|
+
mimeType,
|
|
273
|
+
encoding,
|
|
274
|
+
data: file,
|
|
275
|
+
ext,
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
bb.on('field', (name, value, info) => {
|
|
279
|
+
fieldReadable.push({
|
|
280
|
+
name,
|
|
281
|
+
value,
|
|
282
|
+
info,
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
bb.on('close', () => {
|
|
286
|
+
fileReadable.push(null);
|
|
287
|
+
fieldReadable.push(null);
|
|
288
|
+
});
|
|
289
|
+
bb.on('error', (err) => {
|
|
290
|
+
fileReadable.destroy(new error_1.MultipartError(err));
|
|
291
|
+
});
|
|
292
|
+
bb.on('partsLimit', () => {
|
|
293
|
+
fileReadable.destroy(new error_1.MultipartPartsLimitError());
|
|
294
|
+
});
|
|
295
|
+
bb.on('filesLimit', () => {
|
|
296
|
+
fileReadable.destroy(new error_1.MultipartFileLimitError());
|
|
297
|
+
});
|
|
298
|
+
bb.on('fieldsLimit', () => {
|
|
299
|
+
fieldReadable.destroy(new error_1.MultipartFieldsLimitError());
|
|
300
|
+
});
|
|
301
|
+
req.pipe(bb);
|
|
302
|
+
ctxOrReq.fields = fields;
|
|
303
|
+
ctxOrReq.files = files;
|
|
304
|
+
}
|
|
305
|
+
async processServerlessUpload(req, boundary, uploadConfig, next, currentContextWhiteListMap, currentContextMimeTypeWhiteListMap, ctxOrReq) {
|
|
306
|
+
var _a;
|
|
307
|
+
let body;
|
|
308
|
+
if (((_a = req === null || req === void 0 ? void 0 : req.originEvent) === null || _a === void 0 ? void 0 : _a.body) &&
|
|
309
|
+
(typeof req.originEvent.body === 'string' ||
|
|
310
|
+
Buffer.isBuffer(req.originEvent.body))) {
|
|
311
|
+
body = req.originEvent.body;
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
body = req.body;
|
|
315
|
+
}
|
|
316
|
+
const { mode, tmpdir } = uploadConfig;
|
|
317
|
+
const data = await (0, parse_1.parseMultipart)(body, boundary, uploadConfig);
|
|
318
|
+
if (!data) {
|
|
319
|
+
return next();
|
|
320
|
+
}
|
|
321
|
+
const requireId = `upload_${Date.now()}.${Math.random()}`;
|
|
322
|
+
for (const fileInfo of data.files) {
|
|
323
|
+
const ext = this.checkAndGetExt(fileInfo.filename, currentContextWhiteListMap);
|
|
324
|
+
if (!ext) {
|
|
325
|
+
throw new error_1.MultipartInvalidFilenameError(fileInfo.filename);
|
|
326
|
+
}
|
|
327
|
+
const { passed, mime, current } = await this.checkFileType(ext, fileInfo.data, currentContextMimeTypeWhiteListMap);
|
|
328
|
+
if (!passed) {
|
|
329
|
+
throw new error_1.MultipartInvalidFileTypeError(fileInfo.filename, current, mime);
|
|
330
|
+
}
|
|
331
|
+
fileInfo[constants_1.EXT_KEY] = ext;
|
|
332
|
+
}
|
|
333
|
+
const files = await Promise.all(data.files.map(async (file, index) => {
|
|
334
|
+
const { data } = file;
|
|
335
|
+
if (mode === 'file') {
|
|
336
|
+
const ext = file[constants_1.EXT_KEY];
|
|
337
|
+
const tmpFileName = (0, path_1.resolve)(tmpdir, `${requireId}.${index}${ext}`);
|
|
338
|
+
await writeFile(tmpFileName, data, 'binary');
|
|
339
|
+
file.data = tmpFileName;
|
|
340
|
+
}
|
|
341
|
+
else if (mode === 'stream') {
|
|
342
|
+
file.data = new stream_1.Readable({
|
|
343
|
+
read() {
|
|
344
|
+
this.push(data);
|
|
345
|
+
this.push(null);
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
return file;
|
|
350
|
+
}));
|
|
351
|
+
ctxOrReq.fields = data.fields;
|
|
352
|
+
ctxOrReq.files = files;
|
|
268
353
|
}
|
|
269
354
|
static getName() {
|
|
270
355
|
return 'upload';
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Readable } from 'stream';
|
|
1
3
|
export declare const autoRemoveUploadTmpFile: (tmpDir: string, cleanTimeout: number) => Promise<void>;
|
|
2
4
|
export declare const stopAutoRemoveUploadTmpFile: () => Promise<void>;
|
|
3
5
|
export declare const checkExists: (path: string) => Promise<boolean>;
|
|
4
6
|
export declare const ensureDir: (dirPath: string) => Promise<boolean>;
|
|
5
7
|
export declare const formatExt: (ext: string) => string;
|
|
8
|
+
export declare function streamToAsyncIterator(stream: Readable): AsyncGenerator<any>;
|
|
6
9
|
//# 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.formatExt = exports.ensureDir = exports.checkExists = exports.stopAutoRemoveUploadTmpFile = exports.autoRemoveUploadTmpFile = void 0;
|
|
3
|
+
exports.streamToAsyncIterator = 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;
|
|
@@ -88,4 +88,10 @@ const formatExt = (ext) => {
|
|
|
88
88
|
.toString();
|
|
89
89
|
};
|
|
90
90
|
exports.formatExt = formatExt;
|
|
91
|
+
async function* streamToAsyncIterator(stream) {
|
|
92
|
+
for await (const chunk of stream) {
|
|
93
|
+
yield chunk;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.streamToAsyncIterator = streamToAsyncIterator;
|
|
91
97
|
//# sourceMappingURL=utils.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@midwayjs/busboy",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.18.0",
|
|
4
4
|
"description": "Midway Component for upload",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
"file-type": "16.5.4"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@midwayjs/core": "^3.
|
|
31
|
-
"@midwayjs/express": "^3.
|
|
32
|
-
"@midwayjs/faas": "^3.
|
|
33
|
-
"@midwayjs/koa": "^3.
|
|
34
|
-
"@midwayjs/mock": "^3.
|
|
35
|
-
"@midwayjs/web": "^3.
|
|
30
|
+
"@midwayjs/core": "^3.18.0",
|
|
31
|
+
"@midwayjs/express": "^3.18.0",
|
|
32
|
+
"@midwayjs/faas": "^3.18.0",
|
|
33
|
+
"@midwayjs/koa": "^3.18.0",
|
|
34
|
+
"@midwayjs/mock": "^3.18.0",
|
|
35
|
+
"@midwayjs/web": "^3.18.0"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "5e6180ba0fabe1acc6df112c68855129a534e941"
|
|
38
38
|
}
|