/
multipart.ts
133 lines (118 loc) · 4.58 KB
/
multipart.ts
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import { StringAnyMap } from './../interfaces/types';
import { Deco } from './../interfaces';
import { Model } from './../decorators/model';
import { Settings } from '../helpers/settings';
import multer from 'multer';
import { RequestHandler, Request, Response, NextFunction } from 'express';
import mkdirp from 'mkdirp';
import crypto from 'crypto';
let debug = require('debug')('deco-api:middlewares:multipart');
export interface MultipartRequest extends Request {
filesPropertyOptions?: StringAnyMap;
}
let storage = multer.diskStorage({
destination: function (req, file, cb) {
let options = (req as MultipartRequest).filesPropertyOptions;
let fieldname = file.fieldname;
if (fieldname.substr(Settings.filePreviewSuffix.length * -1) === Settings.filePreviewSuffix) {
fieldname = fieldname.substr(0, fieldname.length - Settings.filePreviewSuffix.length);
}
if (options && options[fieldname] && options[fieldname].destination && typeof options[fieldname].destination === 'string' ) {
mkdirp.sync(options[fieldname].destination);
cb(null, options[fieldname].destination);
} else {
cb(null, 'uploads/')
}
},
filename: function (req, file, cb) {
crypto.pseudoRandomBytes(16, function (err, raw) {
if (err) cb(err, '');
else cb(null, raw.toString('hex'));
});
}
});
let fileFilter = (req: Request, file: any, cb: Function) => {
let options = (req as MultipartRequest).filesPropertyOptions;
let filter = true;
let suff = Settings.filePreviewSuffix;
if (file.fieldname.substr(suff.length * -1) === suff) {
// this is a preview field
let ok = new RegExp('image/*').test(file.mimetype);
cb(null, ok);
return;
}
if (!options || !options[file.fieldname] || !options[file.fieldname].accepted) {
filter = false;
cb(null, true);
return;
} if (options && typeof options[file.fieldname].accepted === 'string') {
options[file.fieldname].accepted = [options[file.fieldname].accepted];
}
if (options && Array.isArray(options[file.fieldname].accepted) && filter) {
let accepted = (options[file.fieldname].accepted || []).join('|');
accepted = accepted.replace(/\*/g, '.*');
accepted = accepted.replace(/\//g, '\/');
accepted = accepted.replace('+', '\\+');
let ok = new RegExp(accepted).test(file.mimetype);
cb(null, ok);
return;
}
}
let uploadEngine = multer({ fileFilter: fileFilter, storage: storage })
export class MultipartMiddleware {
static uploadEngine = uploadEngine;
static parseDeco(deco: Deco, storage: 'fs' | 'gfs' = 'fs'): Array<RequestHandler> {
if (storage !== 'fs') {
throw new Error(`Only 'fs' storage is available at the moment`);
}
// find all file fields
let fileProperties = Model.getDecoProperties(deco, ['file', 'files']);
// return a middleware setup for uploading files from the file files
let fieldsConfig: Array<any> = [];
for (let prop of fileProperties) {
let maxCount = deco.propertyTypes[prop].name === 'file' ? 1 : 12;
if (deco.propertyTypes[prop].name === 'files' && deco.propertyTypesOptions[prop] && deco.propertyTypesOptions[prop].maxCount) {
maxCount = deco.propertyTypesOptions[prop].maxCount;
}
fieldsConfig.push({name: prop, maxCount: maxCount});
fieldsConfig.push({name: `${prop}${Settings.filePreviewSuffix}`, maxCount: 100});
}
return [
MultipartMiddleware.filesOptionsInRequest(deco, fileProperties),
MultipartMiddleware.uploadEngine.fields(fieldsConfig),
MultipartMiddleware.filesInBody
];
}
static filesOptionsInRequest(deco: Deco, fileProperties: Array<string>) {
return (req: MultipartRequest, res: Response, next: NextFunction) => {
// keep a copy of the original body to restore later
res.locals._tmpBody = req.body;
if (!req.filesPropertyOptions) req.filesPropertyOptions = {};
for (let fileProp of fileProperties) {
req.filesPropertyOptions[fileProp] = deco.propertyTypesOptions[fileProp];
}
next();
};
}
static filesInBody(req: Request, res: Response, next: NextFunction) {
if (!req.files) return next();
if (!req.body) req.body = {};
if (res.locals._tmpBody) {
for (let key in res.locals._tmpBody) {
req.body[key] = res.locals._tmpBody[key];
}
}
delete res.locals._tmpBody;
for (let key in req.body) {
try {
let testJsonParse = JSON.parse(req.body[key]);
req.body[key] = testJsonParse;
} catch (e) {
}
}
for (let fileProp in req.files) {
req.body[fileProp] = (req.files as any)[fileProp];
}
next();
}
}