@midwayjs/busboy 3.16.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/README.md +12 -0
- package/dist/configuration.d.ts +9 -0
- package/dist/configuration.js +61 -0
- package/dist/constants.d.ts +24 -0
- package/dist/constants.js +60 -0
- package/dist/error.d.ts +23 -0
- package/dist/error.js +49 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +24 -0
- package/dist/interface.d.ts +68 -0
- package/dist/interface.js +3 -0
- package/dist/middleware.d.ts +34 -0
- package/dist/middleware.js +366 -0
- package/dist/parse.d.ts +10 -0
- package/dist/parse.js +99 -0
- package/dist/utils.d.ts +6 -0
- package/dist/utils.js +91 -0
- package/index.d.ts +39 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# midway busboy module
|
|
2
|
+
|
|
3
|
+
[](http://packagequality.com/#?package=@midwayjs/busboy)
|
|
4
|
+
[](https://github.com/midwayjs/midway/pulls)
|
|
5
|
+
|
|
6
|
+
this is a sub package for midway.
|
|
7
|
+
|
|
8
|
+
Document: [https://midwayjs.org](https://midwayjs.org)
|
|
9
|
+
|
|
10
|
+
## License
|
|
11
|
+
|
|
12
|
+
[MIT]((http://github.com/midwayjs/midway/blob/master/LICENSE))
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ILogger } from '@midwayjs/core';
|
|
2
|
+
import { UploadOptions } from './interface';
|
|
3
|
+
export declare class BusboyConfiguration {
|
|
4
|
+
uploadConfig: UploadOptions;
|
|
5
|
+
logger: ILogger;
|
|
6
|
+
onReady(): Promise<void>;
|
|
7
|
+
onStop(): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=configuration.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.BusboyConfiguration = void 0;
|
|
13
|
+
const core_1 = require("@midwayjs/core");
|
|
14
|
+
const utils_1 = require("./utils");
|
|
15
|
+
const constants_1 = require("./constants");
|
|
16
|
+
const path_1 = require("path");
|
|
17
|
+
const os_1 = require("os");
|
|
18
|
+
let BusboyConfiguration = class BusboyConfiguration {
|
|
19
|
+
async onReady() {
|
|
20
|
+
const { tmpdir, cleanTimeout, mode } = this.uploadConfig;
|
|
21
|
+
if (mode === 'file' && tmpdir) {
|
|
22
|
+
await (0, utils_1.ensureDir)(tmpdir);
|
|
23
|
+
if (cleanTimeout) {
|
|
24
|
+
(0, utils_1.autoRemoveUploadTmpFile)(tmpdir, cleanTimeout).catch(err => {
|
|
25
|
+
this.logger.error(err);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async onStop() {
|
|
31
|
+
await (0, utils_1.stopAutoRemoveUploadTmpFile)();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
__decorate([
|
|
35
|
+
(0, core_1.Config)('busboy'),
|
|
36
|
+
__metadata("design:type", Object)
|
|
37
|
+
], BusboyConfiguration.prototype, "uploadConfig", void 0);
|
|
38
|
+
__decorate([
|
|
39
|
+
(0, core_1.Logger)('coreLogger'),
|
|
40
|
+
__metadata("design:type", Object)
|
|
41
|
+
], BusboyConfiguration.prototype, "logger", void 0);
|
|
42
|
+
BusboyConfiguration = __decorate([
|
|
43
|
+
(0, core_1.Configuration)({
|
|
44
|
+
namespace: 'busboy',
|
|
45
|
+
importConfigs: [
|
|
46
|
+
{
|
|
47
|
+
default: {
|
|
48
|
+
busboy: {
|
|
49
|
+
mode: 'file',
|
|
50
|
+
whitelist: constants_1.uploadWhiteList,
|
|
51
|
+
tmpdir: (0, path_1.join)((0, os_1.tmpdir)(), 'midway-busboy-files'),
|
|
52
|
+
cleanTimeout: 5 * 60 * 1000,
|
|
53
|
+
base64: false,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
})
|
|
59
|
+
], BusboyConfiguration);
|
|
60
|
+
exports.BusboyConfiguration = BusboyConfiguration;
|
|
61
|
+
//# sourceMappingURL=configuration.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
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;
|
|
24
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EXT_KEY = exports.DefaultUploadFileMimeType = exports.uploadWhiteList = void 0;
|
|
4
|
+
exports.uploadWhiteList = [
|
|
5
|
+
// images
|
|
6
|
+
'.jpg',
|
|
7
|
+
'.jpeg',
|
|
8
|
+
'.png',
|
|
9
|
+
'.gif',
|
|
10
|
+
'.bmp',
|
|
11
|
+
'.wbmp',
|
|
12
|
+
'.webp',
|
|
13
|
+
'.tif',
|
|
14
|
+
'.tiff',
|
|
15
|
+
'.psd',
|
|
16
|
+
// text
|
|
17
|
+
'.svg',
|
|
18
|
+
'.js',
|
|
19
|
+
'.jsx',
|
|
20
|
+
'.json',
|
|
21
|
+
'.css',
|
|
22
|
+
'.less',
|
|
23
|
+
'.html',
|
|
24
|
+
'.htm',
|
|
25
|
+
'.xml',
|
|
26
|
+
'.pdf',
|
|
27
|
+
// tar
|
|
28
|
+
'.zip',
|
|
29
|
+
'.gz',
|
|
30
|
+
'.tgz',
|
|
31
|
+
'.gzip',
|
|
32
|
+
// video
|
|
33
|
+
'.mp3',
|
|
34
|
+
'.mp4',
|
|
35
|
+
'.avi',
|
|
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');
|
|
60
|
+
//# sourceMappingURL=constants.js.map
|
package/dist/error.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { httpError } from '@midwayjs/core';
|
|
2
|
+
export declare class MultipartInvalidFilenameError extends httpError.BadRequestError {
|
|
3
|
+
constructor(filename: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class MultipartInvalidFileTypeError extends httpError.BadRequestError {
|
|
6
|
+
constructor(filename: string, currentType: string, type: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class MultipartFileSizeLimitError extends httpError.BadRequestError {
|
|
9
|
+
constructor(filename: string);
|
|
10
|
+
}
|
|
11
|
+
export declare class MultipartError extends httpError.BadRequestError {
|
|
12
|
+
constructor(err: Error);
|
|
13
|
+
}
|
|
14
|
+
export declare class MultipartFileLimitError extends httpError.BadRequestError {
|
|
15
|
+
constructor();
|
|
16
|
+
}
|
|
17
|
+
export declare class MultipartPartsLimitError extends httpError.BadRequestError {
|
|
18
|
+
constructor();
|
|
19
|
+
}
|
|
20
|
+
export declare class MultipartFieldsLimitError extends httpError.BadRequestError {
|
|
21
|
+
constructor();
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=error.d.ts.map
|
package/dist/error.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MultipartFieldsLimitError = exports.MultipartPartsLimitError = exports.MultipartFileLimitError = exports.MultipartError = exports.MultipartFileSizeLimitError = exports.MultipartInvalidFileTypeError = exports.MultipartInvalidFilenameError = void 0;
|
|
4
|
+
const core_1 = require("@midwayjs/core");
|
|
5
|
+
class MultipartInvalidFilenameError extends core_1.httpError.BadRequestError {
|
|
6
|
+
constructor(filename) {
|
|
7
|
+
super(`Invalid upload file name "${filename}", please check it`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
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;
|
|
17
|
+
class MultipartFileSizeLimitError extends core_1.httpError.BadRequestError {
|
|
18
|
+
constructor(filename) {
|
|
19
|
+
super(`Upload file "${filename}" size exceeds the limit`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.MultipartFileSizeLimitError = MultipartFileSizeLimitError;
|
|
23
|
+
class MultipartError extends core_1.httpError.BadRequestError {
|
|
24
|
+
constructor(err) {
|
|
25
|
+
super(err.message);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.MultipartError = MultipartError;
|
|
29
|
+
class MultipartFileLimitError extends core_1.httpError.BadRequestError {
|
|
30
|
+
constructor() {
|
|
31
|
+
super('Upload file count exceeds the limit');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.MultipartFileLimitError = MultipartFileLimitError;
|
|
35
|
+
// partsLimit
|
|
36
|
+
class MultipartPartsLimitError extends core_1.httpError.BadRequestError {
|
|
37
|
+
constructor() {
|
|
38
|
+
super('Upload parts count exceeds the limit');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.MultipartPartsLimitError = MultipartPartsLimitError;
|
|
42
|
+
// fieldsLimit
|
|
43
|
+
class MultipartFieldsLimitError extends core_1.httpError.BadRequestError {
|
|
44
|
+
constructor() {
|
|
45
|
+
super('Upload fields count exceeds the limit');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.MultipartFieldsLimitError = MultipartFieldsLimitError;
|
|
49
|
+
//# sourceMappingURL=error.js.map
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.Configuration = void 0;
|
|
18
|
+
var configuration_1 = require("./configuration");
|
|
19
|
+
Object.defineProperty(exports, "Configuration", { enumerable: true, get: function () { return configuration_1.BusboyConfiguration; } });
|
|
20
|
+
__exportStar(require("./interface"), exports);
|
|
21
|
+
__exportStar(require("./middleware"), exports);
|
|
22
|
+
__exportStar(require("./error"), exports);
|
|
23
|
+
__exportStar(require("./constants"), exports);
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Readable } from 'stream';
|
|
3
|
+
import { IgnoreMatcher, IMidwayContext } from '@midwayjs/core';
|
|
4
|
+
import { BusboyConfig } from 'busboy';
|
|
5
|
+
export type UploadMode = 'stream' | 'file';
|
|
6
|
+
export interface UploadOptions extends BusboyConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Upload mode, default is `file`
|
|
9
|
+
*/
|
|
10
|
+
mode?: UploadMode;
|
|
11
|
+
/**
|
|
12
|
+
* The white ext file names
|
|
13
|
+
*/
|
|
14
|
+
whitelist?: string[] | null | ((ctx: IMidwayContext<any>) => string[]);
|
|
15
|
+
/**
|
|
16
|
+
* Temporary file directory
|
|
17
|
+
*/
|
|
18
|
+
tmpdir?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Temporary file automatic cleanup time, default is 5 minutes
|
|
21
|
+
*/
|
|
22
|
+
cleanTimeout?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Whether the uploaded body is base64, for example, apigw of Tencent Cloud
|
|
25
|
+
*/
|
|
26
|
+
base64?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Which paths to ignore
|
|
29
|
+
*/
|
|
30
|
+
ignore?: IgnoreMatcher<any> | IgnoreMatcher<any>[];
|
|
31
|
+
/**
|
|
32
|
+
* Match those paths with higher priority than ignore
|
|
33
|
+
*/
|
|
34
|
+
match?: IgnoreMatcher<any> | IgnoreMatcher<any>[];
|
|
35
|
+
/**
|
|
36
|
+
* Mime type white list
|
|
37
|
+
*/
|
|
38
|
+
mimeTypeWhiteList?: Record<string, string | string[]> | ((ctx: IMidwayContext<any>) => string | string[]);
|
|
39
|
+
}
|
|
40
|
+
export interface UploadFileInfo {
|
|
41
|
+
/**
|
|
42
|
+
* File name
|
|
43
|
+
*/
|
|
44
|
+
filename: string;
|
|
45
|
+
/**
|
|
46
|
+
* file mime type
|
|
47
|
+
*/
|
|
48
|
+
mimeType: string;
|
|
49
|
+
/**
|
|
50
|
+
* file data, a string of path
|
|
51
|
+
*/
|
|
52
|
+
data: string;
|
|
53
|
+
}
|
|
54
|
+
export interface UploadStreamFileInfo {
|
|
55
|
+
/**
|
|
56
|
+
* File name
|
|
57
|
+
*/
|
|
58
|
+
filename: string;
|
|
59
|
+
/**
|
|
60
|
+
* file mime type
|
|
61
|
+
*/
|
|
62
|
+
mimeType: string;
|
|
63
|
+
/**
|
|
64
|
+
* file data, Readable stream
|
|
65
|
+
*/
|
|
66
|
+
data: Readable;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=interface.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { IMiddleware, ILogger, IgnoreMatcher, IMidwayApplication } from '@midwayjs/core';
|
|
3
|
+
import { UploadOptions } from '.';
|
|
4
|
+
import { BusboyConfig } from 'busboy';
|
|
5
|
+
export declare class UploadMiddleware implements IMiddleware<any, any> {
|
|
6
|
+
uploadConfig: UploadOptions;
|
|
7
|
+
logger: ILogger;
|
|
8
|
+
/**
|
|
9
|
+
* cache global upload white list when uploadConfig.whitelist is set
|
|
10
|
+
* @private
|
|
11
|
+
*/
|
|
12
|
+
private uploadWhiteListMap;
|
|
13
|
+
/**
|
|
14
|
+
* cache global upload mime type white list when uploadConfig.mimeTypeWhiteList is set
|
|
15
|
+
* @private
|
|
16
|
+
*/
|
|
17
|
+
private uploadFileMimeTypeMap;
|
|
18
|
+
match: IgnoreMatcher<any>[];
|
|
19
|
+
ignore: IgnoreMatcher<any>[];
|
|
20
|
+
init(): Promise<void>;
|
|
21
|
+
resolve(app: IMidwayApplication, options?: {
|
|
22
|
+
mode?: 'file' | 'stream';
|
|
23
|
+
} & BusboyConfig): (ctxOrReq: any, resOrNext: any, next: any) => Promise<any>;
|
|
24
|
+
static getName(): string;
|
|
25
|
+
checkAndGetExt(filename: any, whiteListMap?: Map<string, string>): string | boolean;
|
|
26
|
+
checkFileType(ext: string, data: Buffer, uploadFileMimeTypeMap?: Map<string, string[]>): Promise<{
|
|
27
|
+
passed: boolean;
|
|
28
|
+
mime?: string;
|
|
29
|
+
current?: string;
|
|
30
|
+
}>;
|
|
31
|
+
isReadableStream(req: any, isExpress: any): boolean;
|
|
32
|
+
getUploadBoundary(request: any): false | string;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.UploadMiddleware = void 0;
|
|
13
|
+
const core_1 = require("@midwayjs/core");
|
|
14
|
+
const path_1 = require("path");
|
|
15
|
+
const fs_1 = require("fs");
|
|
16
|
+
const stream_1 = require("stream");
|
|
17
|
+
const _1 = require(".");
|
|
18
|
+
const parse_1 = require("./parse");
|
|
19
|
+
const file_type_1 = require("file-type");
|
|
20
|
+
const utils_1 = require("./utils");
|
|
21
|
+
const busboy = require("busboy");
|
|
22
|
+
const { unlink, writeFile } = fs_1.promises;
|
|
23
|
+
let UploadMiddleware = class UploadMiddleware {
|
|
24
|
+
constructor() {
|
|
25
|
+
/**
|
|
26
|
+
* cache global upload white list when uploadConfig.whitelist is set
|
|
27
|
+
* @private
|
|
28
|
+
*/
|
|
29
|
+
this.uploadWhiteListMap = new Map();
|
|
30
|
+
/**
|
|
31
|
+
* cache global upload mime type white list when uploadConfig.mimeTypeWhiteList is set
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
this.uploadFileMimeTypeMap = new Map();
|
|
35
|
+
}
|
|
36
|
+
async init() {
|
|
37
|
+
if (this.uploadConfig.match) {
|
|
38
|
+
this.match = [].concat(this.uploadConfig.match || []);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this.ignore = [].concat(this.uploadConfig.ignore || []);
|
|
42
|
+
}
|
|
43
|
+
if (this.uploadConfig.whitelist &&
|
|
44
|
+
Array.isArray(this.uploadConfig.whitelist)) {
|
|
45
|
+
for (const whiteExt of this.uploadConfig.whitelist) {
|
|
46
|
+
this.uploadWhiteListMap.set(whiteExt, whiteExt);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (this.uploadConfig.mimeTypeWhiteList) {
|
|
50
|
+
for (const ext in this.uploadConfig.mimeTypeWhiteList) {
|
|
51
|
+
const mime = [].concat(this.uploadConfig.mimeTypeWhiteList[ext]);
|
|
52
|
+
this.uploadFileMimeTypeMap.set(ext, mime);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
resolve(app, options) {
|
|
57
|
+
const isExpress = app.getNamespace() === 'express';
|
|
58
|
+
const uploadConfig = options
|
|
59
|
+
? (0, core_1.extend)({}, this.uploadConfig, options || {})
|
|
60
|
+
: this.uploadConfig;
|
|
61
|
+
return async (ctxOrReq, resOrNext, next) => {
|
|
62
|
+
var _a, _b;
|
|
63
|
+
const req = ((_a = ctxOrReq.request) === null || _a === void 0 ? void 0 : _a.req) || ctxOrReq.request || ctxOrReq;
|
|
64
|
+
next = isExpress ? next : resOrNext;
|
|
65
|
+
const boundary = this.getUploadBoundary(req);
|
|
66
|
+
if (!boundary) {
|
|
67
|
+
return next();
|
|
68
|
+
}
|
|
69
|
+
const { mode, tmpdir } = uploadConfig;
|
|
70
|
+
// create new map include custom white list
|
|
71
|
+
const currentContextWhiteListMap = new Map([
|
|
72
|
+
...this.uploadWhiteListMap.entries(),
|
|
73
|
+
]);
|
|
74
|
+
if (typeof uploadConfig.whitelist === 'function') {
|
|
75
|
+
const whiteListArray = uploadConfig.whitelist.call(this, ctxOrReq);
|
|
76
|
+
whiteListArray.forEach(ext => currentContextWhiteListMap.set(ext, ext));
|
|
77
|
+
}
|
|
78
|
+
// create new map include custom mime type white list
|
|
79
|
+
const currentContextMimeTypeWhiteListMap = new Map([
|
|
80
|
+
...this.uploadFileMimeTypeMap.entries(),
|
|
81
|
+
]);
|
|
82
|
+
if (typeof uploadConfig.mimeTypeWhiteList === 'function') {
|
|
83
|
+
const mimeTypeWhiteList = uploadConfig.mimeTypeWhiteList.call(this, ctxOrReq);
|
|
84
|
+
for (const ext in mimeTypeWhiteList) {
|
|
85
|
+
const mime = [].concat(mimeTypeWhiteList[ext]);
|
|
86
|
+
currentContextMimeTypeWhiteListMap.set(ext, mime);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
ctxOrReq.cleanupRequestFiles = async () => {
|
|
90
|
+
var _a;
|
|
91
|
+
if (!((_a = ctxOrReq.files) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
return Promise.all(ctxOrReq.files.map(async (fileInfo) => {
|
|
95
|
+
if (typeof fileInfo.data !== 'string') {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
await unlink(fileInfo.data);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
catch (_a) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}));
|
|
106
|
+
};
|
|
107
|
+
if (this.isReadableStream(req, isExpress)) {
|
|
108
|
+
let isStreamResolve = false;
|
|
109
|
+
const { files = [], fields = [] } = await new Promise((resolveP, reject) => {
|
|
110
|
+
const bb = busboy({
|
|
111
|
+
headers: req.headers,
|
|
112
|
+
...uploadConfig,
|
|
113
|
+
});
|
|
114
|
+
const fields = [];
|
|
115
|
+
const files = [];
|
|
116
|
+
let fileModeCount = 0;
|
|
117
|
+
bb.on('file', async (name, file, info) => {
|
|
118
|
+
const { filename, encoding, mimeType } = info;
|
|
119
|
+
const ext = this.checkAndGetExt(filename, currentContextWhiteListMap);
|
|
120
|
+
if (!ext) {
|
|
121
|
+
reject(new _1.MultipartInvalidFilenameError(filename));
|
|
122
|
+
}
|
|
123
|
+
file.on('limit', () => {
|
|
124
|
+
reject(new _1.MultipartFileSizeLimitError(filename));
|
|
125
|
+
});
|
|
126
|
+
if (mode === 'stream') {
|
|
127
|
+
if (isStreamResolve) {
|
|
128
|
+
// will be skip
|
|
129
|
+
file.resume();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
files.push(new Promise(resolve => {
|
|
133
|
+
resolve({
|
|
134
|
+
filename,
|
|
135
|
+
mimeType,
|
|
136
|
+
encoding,
|
|
137
|
+
data: file,
|
|
138
|
+
});
|
|
139
|
+
}));
|
|
140
|
+
isStreamResolve = true;
|
|
141
|
+
return resolveP({
|
|
142
|
+
fields: await Promise.all(fields),
|
|
143
|
+
files: await Promise.all(files),
|
|
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
|
+
});
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
bb.on('field', (name, value, info) => {
|
|
184
|
+
fields.push(new Promise(resolve => {
|
|
185
|
+
resolve({
|
|
186
|
+
name,
|
|
187
|
+
value,
|
|
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());
|
|
202
|
+
});
|
|
203
|
+
req.pipe(bb);
|
|
204
|
+
});
|
|
205
|
+
ctxOrReq.files = files;
|
|
206
|
+
ctxOrReq.fields = fields.reduce((accumulator, current) => {
|
|
207
|
+
accumulator[current.name] = current.value;
|
|
208
|
+
return accumulator;
|
|
209
|
+
}, {});
|
|
210
|
+
Object.defineProperty(ctxOrReq, 'file', {
|
|
211
|
+
get() {
|
|
212
|
+
return ctxOrReq.files[0];
|
|
213
|
+
},
|
|
214
|
+
set(v) {
|
|
215
|
+
ctxOrReq.files = [v];
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
let body;
|
|
221
|
+
if (((_b = req === null || req === void 0 ? void 0 : req.originEvent) === null || _b === void 0 ? void 0 : _b.body) &&
|
|
222
|
+
(typeof req.originEvent.body === 'string' ||
|
|
223
|
+
Buffer.isBuffer(req.originEvent.body))) {
|
|
224
|
+
body = req.originEvent.body;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
body = req.body;
|
|
228
|
+
}
|
|
229
|
+
const data = await (0, parse_1.parseMultipart)(body, boundary, uploadConfig);
|
|
230
|
+
if (!data) {
|
|
231
|
+
return next();
|
|
232
|
+
}
|
|
233
|
+
ctxOrReq.fields = data.fields;
|
|
234
|
+
const requireId = `upload_${Date.now()}.${Math.random()}`;
|
|
235
|
+
const files = data.files;
|
|
236
|
+
for (const fileInfo of files) {
|
|
237
|
+
const ext = this.checkAndGetExt(fileInfo.filename, currentContextWhiteListMap);
|
|
238
|
+
if (!ext) {
|
|
239
|
+
throw new _1.MultipartInvalidFilenameError(fileInfo.filename);
|
|
240
|
+
}
|
|
241
|
+
const { passed, mime, current } = await this.checkFileType(ext, fileInfo.data, currentContextMimeTypeWhiteListMap);
|
|
242
|
+
if (!passed) {
|
|
243
|
+
throw new _1.MultipartInvalidFileTypeError(fileInfo.filename, current, mime);
|
|
244
|
+
}
|
|
245
|
+
fileInfo[_1.EXT_KEY] = ext;
|
|
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
|
+
}));
|
|
265
|
+
}
|
|
266
|
+
await next();
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
static getName() {
|
|
270
|
+
return 'upload';
|
|
271
|
+
}
|
|
272
|
+
// check extensions
|
|
273
|
+
checkAndGetExt(filename, whiteListMap = this.uploadWhiteListMap) {
|
|
274
|
+
const lowerCaseFileNameList = filename.toLowerCase().split('.');
|
|
275
|
+
while (lowerCaseFileNameList.length) {
|
|
276
|
+
lowerCaseFileNameList.shift();
|
|
277
|
+
const curExt = `.${lowerCaseFileNameList.join('.')}`;
|
|
278
|
+
if (this.uploadConfig.whitelist === null) {
|
|
279
|
+
return (0, utils_1.formatExt)(curExt);
|
|
280
|
+
}
|
|
281
|
+
if (whiteListMap.has(curExt)) {
|
|
282
|
+
// Avoid the presence of hidden characters and return extensions in the white list.
|
|
283
|
+
return whiteListMap.get(curExt);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
// check file-type
|
|
289
|
+
async checkFileType(ext, data, uploadFileMimeTypeMap = this.uploadFileMimeTypeMap) {
|
|
290
|
+
// fileType == null, pass check
|
|
291
|
+
if (!this.uploadConfig.mimeTypeWhiteList) {
|
|
292
|
+
return { passed: true };
|
|
293
|
+
}
|
|
294
|
+
const mime = uploadFileMimeTypeMap.get(ext);
|
|
295
|
+
if (!mime) {
|
|
296
|
+
return { passed: false, mime: ext };
|
|
297
|
+
}
|
|
298
|
+
if (!mime.length) {
|
|
299
|
+
return { passed: true };
|
|
300
|
+
}
|
|
301
|
+
const typeInfo = await (0, file_type_1.fromBuffer)(data);
|
|
302
|
+
if (!typeInfo) {
|
|
303
|
+
return { passed: false, mime: mime.join('、') };
|
|
304
|
+
}
|
|
305
|
+
const findMime = mime.find(mimeItem => mimeItem === typeInfo.mime);
|
|
306
|
+
return {
|
|
307
|
+
passed: !!findMime,
|
|
308
|
+
mime: mime.join('、'),
|
|
309
|
+
current: typeInfo.mime,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
isReadableStream(req, isExpress) {
|
|
313
|
+
// ref: https://github.com/rvagg/isstream/blob/master/isstream.js#L10
|
|
314
|
+
if (req instanceof stream_1.Stream &&
|
|
315
|
+
typeof req._read === 'function' &&
|
|
316
|
+
typeof req._readableState === 'object' &&
|
|
317
|
+
(!req.body || isExpress)) {
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
if (req.pipe && req.on && !req.body) {
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
getUploadBoundary(request) {
|
|
326
|
+
var _a;
|
|
327
|
+
const method = (request.method || request.httpMethod || '').toUpperCase();
|
|
328
|
+
if (method !== 'POST' &&
|
|
329
|
+
method !== 'PUT' &&
|
|
330
|
+
method !== 'DELETE' &&
|
|
331
|
+
method !== 'PATCH') {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
if (!((_a = request.headers) === null || _a === void 0 ? void 0 : _a['content-type'])) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
const contentType = request.headers['content-type'];
|
|
338
|
+
if (!contentType.startsWith('multipart/form-data;')) {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
const boundaryMatch = /boundary=(.*)(;|\s|$)/.exec(contentType);
|
|
342
|
+
if (!(boundaryMatch === null || boundaryMatch === void 0 ? void 0 : boundaryMatch[1])) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
return boundaryMatch[1];
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
__decorate([
|
|
349
|
+
(0, core_1.Config)('busboy'),
|
|
350
|
+
__metadata("design:type", Object)
|
|
351
|
+
], UploadMiddleware.prototype, "uploadConfig", void 0);
|
|
352
|
+
__decorate([
|
|
353
|
+
(0, core_1.Logger)(),
|
|
354
|
+
__metadata("design:type", Object)
|
|
355
|
+
], UploadMiddleware.prototype, "logger", void 0);
|
|
356
|
+
__decorate([
|
|
357
|
+
(0, core_1.Init)(),
|
|
358
|
+
__metadata("design:type", Function),
|
|
359
|
+
__metadata("design:paramtypes", []),
|
|
360
|
+
__metadata("design:returntype", Promise)
|
|
361
|
+
], UploadMiddleware.prototype, "init", null);
|
|
362
|
+
UploadMiddleware = __decorate([
|
|
363
|
+
(0, core_1.Middleware)()
|
|
364
|
+
], UploadMiddleware);
|
|
365
|
+
exports.UploadMiddleware = UploadMiddleware;
|
|
366
|
+
//# sourceMappingURL=middleware.js.map
|
package/dist/parse.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { UploadOptions } from './interface';
|
|
3
|
+
export declare const parseMultipart: (body: any, boundary: string, uploadConfig: UploadOptions) => Promise<{
|
|
4
|
+
files: any[];
|
|
5
|
+
fields: {};
|
|
6
|
+
}>;
|
|
7
|
+
export declare const bufferIndexOf: (buffer: Buffer, search: Buffer, offset?: number) => number;
|
|
8
|
+
export declare const bufferSplit: (buffer: Buffer, separator: Buffer, limit?: number) => Buffer[];
|
|
9
|
+
export declare const parseHead: (headBuf: Buffer) => {};
|
|
10
|
+
//# sourceMappingURL=parse.d.ts.map
|
package/dist/parse.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseHead = exports.bufferSplit = exports.bufferIndexOf = exports.parseMultipart = void 0;
|
|
4
|
+
const headSeparator = Buffer.from('\r\n\r\n');
|
|
5
|
+
const parseMultipart = async (body, boundary, uploadConfig) => {
|
|
6
|
+
if (typeof body === 'string') {
|
|
7
|
+
if (uploadConfig.base64) {
|
|
8
|
+
body = Buffer.from(body, 'base64');
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
body = Buffer.from(body);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const bufferSeparator = Buffer.from('\r\n--' + boundary);
|
|
15
|
+
const fields = {};
|
|
16
|
+
const files = [];
|
|
17
|
+
(0, exports.bufferSplit)(body, bufferSeparator).forEach(buf => {
|
|
18
|
+
const [headerBuf, data] = (0, exports.bufferSplit)(buf, headSeparator, 2);
|
|
19
|
+
const head = (0, exports.parseHead)(headerBuf);
|
|
20
|
+
if (!head['content-disposition']) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (!head['content-disposition'].filename) {
|
|
24
|
+
if (head['content-disposition'].name) {
|
|
25
|
+
fields[head['content-disposition'].name] = data.toString();
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
files.push({
|
|
30
|
+
filename: head['content-disposition'].filename,
|
|
31
|
+
data,
|
|
32
|
+
fieldName: head['content-disposition'].name,
|
|
33
|
+
mimeType: head['content-type'],
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
files,
|
|
38
|
+
fields,
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
exports.parseMultipart = parseMultipart;
|
|
42
|
+
// search buffer index
|
|
43
|
+
const bufferIndexOf = (buffer, search, offset) => {
|
|
44
|
+
return buffer.indexOf(search, offset);
|
|
45
|
+
};
|
|
46
|
+
exports.bufferIndexOf = bufferIndexOf;
|
|
47
|
+
// split buffer to buffer list
|
|
48
|
+
const bufferSplit = (buffer, separator, limit) => {
|
|
49
|
+
let index = 0;
|
|
50
|
+
const result = [];
|
|
51
|
+
let find = (0, exports.bufferIndexOf)(buffer, separator, index);
|
|
52
|
+
while (find !== -1) {
|
|
53
|
+
result.push(buffer.slice(index, find));
|
|
54
|
+
index = find + separator.length;
|
|
55
|
+
if (limit && result.length + 1 === limit) {
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
find = (0, exports.bufferIndexOf)(buffer, separator, index);
|
|
59
|
+
}
|
|
60
|
+
result.push(buffer.slice(index));
|
|
61
|
+
return result;
|
|
62
|
+
};
|
|
63
|
+
exports.bufferSplit = bufferSplit;
|
|
64
|
+
const headReg = /^([^:]+):[ \t]?(.+)?$/;
|
|
65
|
+
const parseHead = (headBuf) => {
|
|
66
|
+
const head = {};
|
|
67
|
+
const headStrList = headBuf.toString().split('\r\n');
|
|
68
|
+
for (const headStr of headStrList) {
|
|
69
|
+
const matched = headReg.exec(headStr);
|
|
70
|
+
if (!matched) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const name = matched[1].toLowerCase();
|
|
74
|
+
const value = matched[2]
|
|
75
|
+
? matched[2].replace(/&#(\d+);/g, (origin, code) => {
|
|
76
|
+
try {
|
|
77
|
+
return String.fromCharCode(parseInt(code));
|
|
78
|
+
}
|
|
79
|
+
catch (_a) {
|
|
80
|
+
return origin;
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
: '';
|
|
84
|
+
if (name === 'content-disposition') {
|
|
85
|
+
const headCol = {};
|
|
86
|
+
value.split(/;\s+/).forEach((kv) => {
|
|
87
|
+
const [k, v] = kv.split('=');
|
|
88
|
+
headCol[k] = v ? v.replace(/^"/, '').replace(/"$/, '') : v !== null && v !== void 0 ? v : true;
|
|
89
|
+
});
|
|
90
|
+
head[name] = headCol;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
head[name] = value;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return head;
|
|
97
|
+
};
|
|
98
|
+
exports.parseHead = parseHead;
|
|
99
|
+
//# sourceMappingURL=parse.js.map
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const autoRemoveUploadTmpFile: (tmpDir: string, cleanTimeout: number) => Promise<void>;
|
|
2
|
+
export declare const stopAutoRemoveUploadTmpFile: () => Promise<void>;
|
|
3
|
+
export declare const checkExists: (path: string) => Promise<boolean>;
|
|
4
|
+
export declare const ensureDir: (dirPath: string) => Promise<boolean>;
|
|
5
|
+
export declare const formatExt: (ext: string) => string;
|
|
6
|
+
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatExt = exports.ensureDir = exports.checkExists = exports.stopAutoRemoveUploadTmpFile = exports.autoRemoveUploadTmpFile = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const { readdir, access, stat, unlink, mkdir } = fs_1.promises;
|
|
7
|
+
let autoRemoveUploadTmpFileTimeoutHandler;
|
|
8
|
+
let autoRemoveUploadTmpFilePromise;
|
|
9
|
+
const autoRemoveUploadTmpFile = async (tmpDir, cleanTimeout) => {
|
|
10
|
+
clearTimeout(autoRemoveUploadTmpFileTimeoutHandler);
|
|
11
|
+
let waitTime = cleanTimeout / 3;
|
|
12
|
+
if (waitTime < 1000) {
|
|
13
|
+
waitTime = 1000;
|
|
14
|
+
}
|
|
15
|
+
if (autoRemoveUploadTmpFilePromise) {
|
|
16
|
+
const exists = await (0, exports.checkExists)(tmpDir);
|
|
17
|
+
if (exists) {
|
|
18
|
+
const paths = await readdir(tmpDir);
|
|
19
|
+
const now = Date.now();
|
|
20
|
+
await Promise.all(paths.map(async (path) => {
|
|
21
|
+
const filePath = (0, path_1.join)(tmpDir, path);
|
|
22
|
+
try {
|
|
23
|
+
const statInfo = await stat(filePath);
|
|
24
|
+
if (statInfo.isFile() && now - statInfo.ctimeMs > cleanTimeout) {
|
|
25
|
+
await unlink(filePath);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch (_a) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
autoRemoveUploadTmpFileTimeoutHandler = setTimeout(() => {
|
|
35
|
+
autoRemoveUploadTmpFilePromise = (0, exports.autoRemoveUploadTmpFile)(tmpDir, cleanTimeout);
|
|
36
|
+
}, waitTime);
|
|
37
|
+
};
|
|
38
|
+
exports.autoRemoveUploadTmpFile = autoRemoveUploadTmpFile;
|
|
39
|
+
const stopAutoRemoveUploadTmpFile = async () => {
|
|
40
|
+
if (autoRemoveUploadTmpFilePromise) {
|
|
41
|
+
await autoRemoveUploadTmpFilePromise;
|
|
42
|
+
autoRemoveUploadTmpFilePromise = null;
|
|
43
|
+
}
|
|
44
|
+
clearTimeout(autoRemoveUploadTmpFileTimeoutHandler);
|
|
45
|
+
};
|
|
46
|
+
exports.stopAutoRemoveUploadTmpFile = stopAutoRemoveUploadTmpFile;
|
|
47
|
+
const checkExists = async (path) => {
|
|
48
|
+
try {
|
|
49
|
+
await access(path, fs_1.constants.W_OK | fs_1.constants.R_OK);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch (_a) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
exports.checkExists = checkExists;
|
|
57
|
+
const ensureDir = async (dirPath) => {
|
|
58
|
+
const isExists = await (0, exports.checkExists)(dirPath);
|
|
59
|
+
if (isExists) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
await mkdir(dirPath, { recursive: true });
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
catch (_a) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
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;
|
|
91
|
+
//# sourceMappingURL=utils.js.map
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { UploadFileInfo, UploadOptions } from './dist/index';
|
|
2
|
+
export * from './dist/index';
|
|
3
|
+
|
|
4
|
+
declare module '@midwayjs/core/dist/interface' {
|
|
5
|
+
interface MidwayConfig {
|
|
6
|
+
busboy?: Partial<UploadOptions>;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
declare module '@midwayjs/koa/dist/interface' {
|
|
10
|
+
interface Context {
|
|
11
|
+
files?: UploadFileInfo<any>[];
|
|
12
|
+
fields?: { [fieldName: string]: any };
|
|
13
|
+
cleanupRequestFiles?: () => Promise<Array<boolean>>;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
declare module '@midwayjs/web/dist/interface' {
|
|
18
|
+
interface Context {
|
|
19
|
+
files?: UploadFileInfo<any>[];
|
|
20
|
+
fields?: { [fieldName: string]: any };
|
|
21
|
+
cleanupRequestFiles?: () => Promise<Array<boolean>>;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare module '@midwayjs/faas/dist/interface' {
|
|
26
|
+
interface Context {
|
|
27
|
+
files?: UploadFileInfo<any>[];
|
|
28
|
+
fields?: { [fieldName: string]: any };
|
|
29
|
+
cleanupRequestFiles?: () => Promise<Array<boolean>>;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
declare module '@midwayjs/express/dist/interface' {
|
|
34
|
+
interface Context {
|
|
35
|
+
files?: UploadFileInfo<any>[];
|
|
36
|
+
fields?: { [fieldName: string]: any };
|
|
37
|
+
cleanupRequestFiles?: () => Promise<Array<boolean>>;
|
|
38
|
+
}
|
|
39
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@midwayjs/busboy",
|
|
3
|
+
"version": "3.16.0",
|
|
4
|
+
"description": "Midway Component for upload",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"typings": "index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"test": "node --require=ts-node/register ../../node_modules/.bin/jest --runInBand",
|
|
10
|
+
"cov": "node --require=ts-node/register ../../node_modules/.bin/jest --runInBand --coverage --forceExit",
|
|
11
|
+
"ci": "npm run test"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [],
|
|
14
|
+
"author": "",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist/**/*.js",
|
|
17
|
+
"dist/**/*.d.ts",
|
|
18
|
+
"index.d.ts"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=12"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"file-type": "16.5.4",
|
|
26
|
+
"busboy": "1.6.0",
|
|
27
|
+
"@types/busboy": "1.5.4"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@midwayjs/core": "^3.16.2",
|
|
31
|
+
"@midwayjs/express": "^3.16.2",
|
|
32
|
+
"@midwayjs/faas": "^3.16.2",
|
|
33
|
+
"@midwayjs/koa": "^3.16.2",
|
|
34
|
+
"@midwayjs/mock": "^3.16.2",
|
|
35
|
+
"@midwayjs/web": "^3.16.2"
|
|
36
|
+
}
|
|
37
|
+
}
|