forked from expressjs/connect-multiparty
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
140 lines (114 loc) · 3.65 KB
/
index.js
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
134
135
136
137
138
139
140
/*!
* connect-multiparty
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* Copyright(c) 2013 Andrew Kelley
* MIT Licensed
*/
'use strict'
/**
* Module dependencies.
* @private
*/
var createError = require('http-errors')
var multiparty = require('multiparty');
var onFinished = require('on-finished');
var qs = require('qs');
var typeis = require('type-is');
/**
* Module exports.
* @public
*/
module.exports = multipart
/**
* Parse multipart/form-data request bodies, providing the parsed
* object as `req.body` and `req.files`.
*
* The options passed are merged with [multiparty](https://github.com/pillarjs/multiparty)'s
* `Form` object, allowing you to configure the upload directory,
* size limits, etc. For example if you wish to change the upload
* dir do the following:
*
* app.use(multipart({ uploadDir: path }))
*
* @param {Object} options
* @return {Function}
* @public
*/
function multipart (options) {
options = options || {};
// Use default max size of 100 MB for all files combined, roughly matching the default
// multipart request size limit of 100 MiB in Express 3 / connect@2.30.2:
// https://github.com/senchalabs/connect/blob/2.30.2/lib/middleware/multipart.js#L86
options.maxFilesSize = options.maxFilesSize || 100 * 1000 * 1000;
return function multipart(req, res, next) {
if (req._body) return next();
req.body = req.body || {};
req.files = req.files || {};
// ignore GET
if ('GET' === req.method || 'HEAD' === req.method) return next();
// check Content-Type
if (!typeis(req, 'multipart/form-data')) return next();
// flag as parsed
req._body = true;
// parse
var form = new multiparty.Form(options);
var data = {};
var files = {};
var done = false;
function ondata(name, val, data){
if (Array.isArray(data[name])) {
data[name].push(val);
} else if (data[name]) {
data[name] = [data[name], val];
} else {
data[name] = val;
}
}
form.on('field', function(name, val){
ondata(name, val, data);
});
form.on('file', function(name, val){
val.name = val.originalFilename;
val.type = val.headers['content-type'] || null;
ondata(name, val, files);
});
form.on('error', function(err){
if (done) return;
done = true;
// set status code on error
var error = createError(400, err)
if (!req.readable) return next(error)
// read off entire request
req.resume();
onFinished(req, function(){
next(error)
});
});
form.on('close', function() {
if (done) return;
done = true;
// expand names with qs & assign
// Note: `allowDots: false, allowPrototypes: true` come from Express 3 / connect@2.30.2:
// https://github.com/senchalabs/connect/blob/2.30.2/lib/middleware/multipart.js#L148-L149
req.body = qs.parse(data, { allowDots: false, allowPrototypes: true })
/**
* Dictionary of field name to FileInfo
* @type {{[fieldName: string]: FileInfo}}
*/
req.files = qs.parse(files, { allowDots: false, allowPrototypes: true })
next()
});
form.parse(req);
}
};
/**
* @typedef {object} FileInfo - Uploaded file from a multipart form
*
* @property {string} name - original filename, from user's system
* @property {string} originalFilename - original filename, from user's system
* @property {string} type - content-type of the file, from user's browser
* @property {number} size - size of the file in bytes
* @property {string} path - local filesystem path to uploaded file
* @property {string} fieldName - name of the form field
*/