1 | /*!
|
2 | Kettle wrapping for Multer Express Middleware
|
3 |
|
4 | Copyright 2017-2018 OCAD University
|
5 |
|
6 | Licensed under the New BSD license. You may not use this file except in
|
7 | compliance with this License.
|
8 |
|
9 | You may obtain a copy of the License at
|
10 | https://github.com/fluid-project/kettle/blob/master/LICENSE.txt
|
11 | */
|
12 |
|
13 | ;
|
14 |
|
15 | // Wraps the standard Express Multer middleware, for handling multipart/form-data
|
16 | // (primarily for file uploads)
|
17 |
|
18 | var fluid = require("infusion"),
|
19 | kettle = fluid.registerNamespace("kettle");
|
20 |
|
21 | fluid.require("multer", require, "kettle.npm.multer");
|
22 |
|
23 | fluid.defaults("kettle.middleware.multer", {
|
24 | gradeNames: "kettle.plainAsyncMiddleware",
|
25 | // See https://github.com/expressjs/multer#multeropts;
|
26 | // Also see the storage and fileFilter components for supplying
|
27 | // configuration to Multer's storage and fileFilter options
|
28 | middlewareOptions: {
|
29 | // These are the default limits multer uses
|
30 | // See https://github.com/expressjs/multer#limits
|
31 | // Recommended that these should be configured on a per-handler basis
|
32 | // to prevent possible DDOS attacks via form submission, or similar
|
33 | limits: {
|
34 | fieldNameSize: 100,
|
35 | fieldSize: "1MB",
|
36 | fields: Infinity,
|
37 | fileSize: Infinity,
|
38 | files: Infinity,
|
39 | parts: Infinity,
|
40 | headerPairs: 2000
|
41 | }
|
42 | },
|
43 | components: {
|
44 | storage: {
|
45 | type: "kettle.middleware.multer.storage.memory"
|
46 | },
|
47 | fileFilter: {
|
48 | type: "kettle.middleware.multer.filter.allowAll"
|
49 | }
|
50 | },
|
51 | // Configures the expected form fields to be handled, and the multer method
|
52 | // to handle them; see https://github.com/expressjs/multer#usage for more
|
53 | // documentation
|
54 | formFieldOptions: {
|
55 | method: "single",
|
56 | // Relevant only for "single" and "array" methods
|
57 | fieldName: "file"
|
58 | // Relevant only for "array" method
|
59 | // maxCount: 10
|
60 | // Relevant only for "fields" method
|
61 | // fields: [
|
62 | // {name: "avatar", maxCount: 1},
|
63 | // {name: "gallery", maxCount: 8}
|
64 | // ]
|
65 | },
|
66 | middleware: "@expand:kettle.middleware.multer.createMiddleware({that}, {that}.options.middlewareOptions, {that}.storage, {that}.options.formFieldOptions, {that}.fileFilter)"
|
67 | });
|
68 |
|
69 | kettle.middleware.multer.createMiddleware = function (that, middlewareOptions, storage, formFieldOptions, fileFilter) {
|
70 |
|
71 | middlewareOptions.storage = storage.multerStorage();
|
72 | middlewareOptions.fileFilter = fileFilter.multerFileFilterResolver;
|
73 |
|
74 | var multer = kettle.npm.multer(middlewareOptions);
|
75 |
|
76 | var multerMethod = formFieldOptions.method;
|
77 |
|
78 | var multerFields = multerMethod === "fields" ? formFieldOptions.fields : formFieldOptions.fieldName;
|
79 |
|
80 | return multerMethod === "array" ? multer[multerMethod](multerFields, formFieldOptions.maxCount) : multer[multerMethod](multerFields);
|
81 | };
|
82 |
|
83 | fluid.defaults("kettle.middleware.multer.filter", {
|
84 | gradeNames: ["fluid.component"],
|
85 | // This must resolve to a filter function in Multer's
|
86 | // expected style - see https://github.com/expressjs/multer#filefilter
|
87 | //
|
88 | // See the mimeType filter for an example of using argument-shifting
|
89 | // to supply configuration material while maintaining Multer's expected
|
90 | // signature
|
91 | invokers: {
|
92 | multerFileFilterResolver: {
|
93 | funcName: "fluid.notImplemented"
|
94 | }
|
95 | }
|
96 | });
|
97 |
|
98 | fluid.defaults("kettle.middleware.multer.filter.allowAll", {
|
99 | gradeNames: ["fluid.component"],
|
100 | invokers: {
|
101 | multerFileFilterResolver: {
|
102 | funcName: "kettle.middleware.multer.filter.allowAll.filterFunc"
|
103 | }
|
104 | }
|
105 | });
|
106 |
|
107 | // Duplicate of Multer's internal allowAll function; Multer doesn't export it,
|
108 | // making it inaccessible to the kettle.npm.multer namespace
|
109 |
|
110 | kettle.middleware.multer.filter.allowAll.filterFunc = function (req, file, cb) {
|
111 | cb(null, true);
|
112 | };
|
113 |
|
114 | fluid.defaults("kettle.middleware.multer.filter.mimeType", {
|
115 | gradeNames: ["fluid.component"],
|
116 | // an array of mimetypes to accept, FE:
|
117 | // acceptedMimeTypes: ["image/gif", "image/jpg", "image/gif"],
|
118 | acceptedMimeTypes: [],
|
119 | invokers: {
|
120 | multerFileFilterResolver: {
|
121 | funcName: "kettle.middleware.multer.filter.mimeType.filterFunc",
|
122 | args: ["{arguments}.0", "{arguments}.1", "{that}.options.acceptedMimeTypes", "{arguments}.2"]
|
123 | }
|
124 | }
|
125 | });
|
126 |
|
127 | // Filters by mimeType based on a list of accepted mime types
|
128 | kettle.middleware.multer.filter.mimeType.filterFunc = function (req, file, acceptedMimeTypes, cb) {
|
129 | var isAcceptableMimeType = fluid.contains(acceptedMimeTypes, file.mimetype);
|
130 | cb(null, isAcceptableMimeType);
|
131 | };
|
132 |
|
133 | // Abstract grade for storage
|
134 | fluid.defaults("kettle.middleware.multer.storage", {
|
135 | gradeNames: ["fluid.component"],
|
136 | // This must be one of the supported Multer storage functions (memoryStorage
|
137 | // or diskStorage, unless using additional plug-ins for Multer); see the
|
138 | // concrete grades below
|
139 | invokers: {
|
140 | multerStorage: {
|
141 | funcName: "fluid.notImplemented"
|
142 | }
|
143 | }
|
144 | });
|
145 |
|
146 | // Storage grade using Multer's memoryStorage storage function - uploaded files
|
147 | // are only stored in memory for the duration of the request, although a handler
|
148 | // or other middleware can process them from request.req.file or
|
149 | // request.req.files
|
150 | fluid.defaults("kettle.middleware.multer.storage.memory", {
|
151 | gradeNames: ["kettle.middleware.multer.storage"],
|
152 | invokers: {
|
153 | multerStorage: {
|
154 | funcName: "kettle.npm.multer.memoryStorage",
|
155 | args: []
|
156 | }
|
157 | }
|
158 | });
|
159 |
|
160 | // Storage grade using Multer's diskStorage storage function, with destination
|
161 | // and filename as configurable options
|
162 | //
|
163 | // This grade should be adequate for most common uses of disk storage - complex
|
164 | // uses should derive from this grade and replace the destination and filename
|
165 | // invokers as necessary to return appropriate functions in Multer's expected
|
166 | // style
|
167 | fluid.defaults("kettle.middleware.multer.storage.disk", {
|
168 | gradeNames: ["kettle.middleware.multer.storage"],
|
169 | // "destination" can be a plain string or a string using fluid.stringTemplate
|
170 | // syntax - the template will receive the "file information" key-values
|
171 | // described at https://github.com/expressjs/multer#file-information
|
172 | destination: "./tests/data/uploads",
|
173 | // "filename" should be a string using fluid.stringTemplate
|
174 | // syntax - the template will receive the "file information" key-values
|
175 | // described at https://github.com/expressjs/multer#file-information
|
176 | //
|
177 | // Note that simply using a plain string will result in every uploaded
|
178 | // file overwriting the previous one - this is unlikely to be desired
|
179 | // behaviour
|
180 | filename: "%originalname",
|
181 | invokers: {
|
182 | // Invokers overriding the "destinationResolver" and "filenameResolver" functions should
|
183 | // use argument-shifting as below to supply a function to Multer with its
|
184 | // expected signature of (req, file, cb)
|
185 | // See https://github.com/expressjs/multer#diskstorage
|
186 | destinationResolver: {
|
187 | funcName: "kettle.middleware.multer.storage.disk.destinationResolver",
|
188 | args: ["{arguments}.0", "{arguments}.1", "{that}.options.destination", "{arguments}.2"]
|
189 | },
|
190 | filenameResolver: {
|
191 | funcName: "kettle.middleware.multer.storage.disk.filenameResolver",
|
192 | args: ["{arguments}.0", "{arguments}.1", "{that}.options.filename", "{arguments}.2"]
|
193 | },
|
194 | multerStorage: {
|
195 | funcName: "kettle.npm.multer.diskStorage",
|
196 | "args": [{
|
197 | destination: "{that}.destinationResolver",
|
198 | filename: "{that}.filenameResolver"
|
199 | }]
|
200 | }
|
201 | }
|
202 | });
|
203 |
|
204 | kettle.middleware.multer.storage.disk.destinationResolver = function (req, file, destinationTemplate, cb) {
|
205 | var destination = fluid.stringTemplate(destinationTemplate, file);
|
206 | cb(null, destination);
|
207 | };
|
208 |
|
209 | kettle.middleware.multer.storage.disk.filenameResolver = function (req, file, filenameTemplate, cb) {
|
210 | var filename = fluid.stringTemplate(filenameTemplate, file);
|
211 | cb(null, filename);
|
212 | };
|