Skip to content

Commit 92d7ad5

Browse files
committed
v0.1.0 Beta: No more urlencoded support. Multipart only. Removed connect-busboy dependency in exchange for directly using Busboy. Updated README with breaking changes. This commit also fixes #11
1 parent 3d72084 commit 92d7ad5

File tree

4 files changed

+169
-99
lines changed

4 files changed

+169
-99
lines changed

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
# Description
22
Simple express middleware for uploading files.
33

4+
# Version 0.1.0 Breaking Change
5+
As of `v0.1.0`, there is NO MORE `application/x-www-form-urlencoded` SUPPORT!
6+
7+
If you want to parse `urlencoded` requests, [use body-parser](https://github.com/expressjs/body-parser#bodyparserurlencodedoptions).
8+
9+
Moving forward, express-fileupload is considered a "multipart" solution only.
10+
411
# Install
512
```bash
613
# With NPM
@@ -113,10 +120,6 @@ Option | Acceptable Values | Details
113120
--- | --- | ---
114121
safeFileNames | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></li><li>regex</li></ul> | Strips characters from the upload's filename. You can use custom regex to determine what to strip. If set to `true`, non-alphanumeric characters _except_ dashes and underscores will be stripped. This option is off by default.<br /><br />**Example #1 (strip slashes from file names):** `app.use(fileUpload({ safeFileNames: /\\/g }))`<br />**Example #2:** `app.use(fileUpload({ safeFileNames: true }))`
115122

116-
# Known Bugs
117-
##### If you're using bodyParser middleware
118-
Add `app.use(fileUpload())` *AFTER* `app.use(bodyParser.json)` and any other `bodyParser` middlewares! This limitation will be investigated in an upcoming release.
119-
120123
# Help Wanted
121124
Pull Requests are welcomed!
122125

lib/index.js

Lines changed: 158 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,169 @@
1-
var busboy = require('connect-busboy');
1+
var Busboy = require('busboy');
22
var fs = require('fs-extra');
33
var streamifier = require('streamifier');
44

5+
var ACCEPTABLE_MIME = /^(?:multipart\/.+)$/i;
6+
var UNACCEPTABLE_METHODS = [
7+
'GET',
8+
'HEAD'
9+
];
10+
511
module.exports = function(options) {
612
options = options || {};
713

814
return function(req, res, next) {
9-
return busboy(options)(req, res, function() {
10-
11-
// If no busboy req obj, then no uploads are taking place
12-
if (!req.busboy)
15+
if (!hasBody(req) || !hasAcceptableMethod(req) || !hasAcceptableMime(req))
1316
return next();
1417

15-
req.files = null;
16-
17-
req.busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated, encoding, mimetype) {
18-
req.body = req.body || {};
19-
20-
var prev = req.body[fieldname];
21-
22-
if (!prev)
23-
return req.body[fieldname] = val;
24-
25-
if (Array.isArray(prev))
26-
return prev.push(val);
27-
28-
req.body[fieldname] = [prev, val];
29-
});
30-
31-
req.busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
32-
var buf = new Buffer(0);
33-
var safeFileNameRegex = /[^\w-]/g;
34-
35-
file.on('data', function(data) {
36-
buf = Buffer.concat([buf, data]);
37-
38-
if (options.debug)
39-
return console.log('Uploading %s -> %s', fieldname, filename);
40-
});
41-
42-
file.on('end', function() {
43-
if (!req.files)
44-
req.files = {};
45-
46-
// see: https://github.com/richardgirges/express-fileupload/issues/14
47-
// firefox uploads empty file in case of cache miss when f5ing page.
48-
// resulting in unexpected behavior. if there is no file data, the file is invalid.
49-
if(!buf.length)
50-
return;
51-
52-
if (options.safeFileNames) {
53-
if (typeof options.safeFileNames === 'object')
54-
safeFileNameRegex = options.safeFileNames;
55-
56-
filename = filename.replace(safeFileNameRegex, '');
57-
}
58-
59-
var newFile = {
60-
name: filename,
61-
data: buf,
62-
encoding: encoding,
63-
mimetype: mimetype,
64-
mv: function(path, callback) {
65-
var fstream;
66-
fstream = fs.createWriteStream(path);
67-
streamifier.createReadStream(buf).pipe(fstream);
68-
fstream.on('error', function(error) {
69-
if (callback)
70-
callback(error);
71-
});
72-
fstream.on('close', function() {
73-
if (callback)
74-
callback(null);
75-
});
76-
}
77-
};
78-
79-
if (!req.files.hasOwnProperty(fieldname)) {
80-
req.files[fieldname] = newFile;
81-
} else {
82-
if (req.files[fieldname] instanceof Array)
83-
req.files[fieldname].push(newFile);
84-
else
85-
req.files[fieldname] = [req.files[fieldname], newFile];
86-
}
87-
});
88-
});
89-
90-
req.busboy.on('finish', next);
91-
92-
req.pipe(req.busboy);
93-
});
18+
processMultipart(options, req, res, next);
9419
};
9520
};
21+
22+
23+
/**
24+
* Processes multipart request
25+
* Builds a req.body object for fields
26+
* Builds a req.files object for files
27+
* @param {Object} options expressFileupload and Busboy options
28+
* @param {Object} req Express request object
29+
* @param {Object} res Express response object
30+
* @param {Function} next Express next method
31+
* @return {void}
32+
*/
33+
function processMultipart(options, req, res, next) {
34+
var busboyOptions = {};
35+
var busboy;
36+
37+
req.files = null;
38+
39+
// Build busboy config
40+
for (var k in options) {
41+
busboyOptions[k] = options[k];
42+
}
43+
44+
// Attach request headers to busboy config
45+
busboyOptions.headers = req.headers;
46+
47+
// Init busboy instance
48+
busboy = new Busboy(busboyOptions);
49+
50+
// Build multipart req.body fields
51+
busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated, encoding, mime) {
52+
req.body = req.body || {};
53+
54+
var prev = req.body[fieldname];
55+
56+
if (!prev)
57+
return req.body[fieldname] = val;
58+
59+
if (Array.isArray(prev))
60+
return prev.push(val);
61+
62+
req.body[fieldname] = [prev, val];
63+
});
64+
65+
// Build req.files fields
66+
busboy.on('file', function(fieldname, file, filename, encoding, mime) {
67+
var buf = new Buffer(0);
68+
var safeFileNameRegex = /[^\w-]/g;
69+
70+
file.on('data', function(data) {
71+
buf = Buffer.concat([buf, data]);
72+
73+
if (options.debug)
74+
return console.log('Uploading %s -> %s', fieldname, filename);
75+
});
76+
77+
file.on('end', function() {
78+
if (!req.files)
79+
req.files = {};
80+
81+
// see: https://github.com/richardgirges/express-fileupload/issues/14
82+
// firefox uploads empty file in case of cache miss when f5ing page.
83+
// resulting in unexpected behavior. if there is no file data, the file is invalid.
84+
if(!buf.length)
85+
return;
86+
87+
if (options.safeFileNames) {
88+
if (typeof options.safeFileNames === 'object')
89+
safeFileNameRegex = options.safeFileNames;
90+
91+
filename = filename.replace(safeFileNameRegex, '');
92+
}
93+
94+
var newFile = {
95+
name: filename,
96+
data: buf,
97+
encoding: encoding,
98+
mimetype: mime,
99+
mv: function(path, callback) {
100+
var fstream = fs.createWriteStream(path);
101+
102+
streamifier.createReadStream(buf).pipe(fstream);
103+
104+
fstream.on('error', function(error) {
105+
if (callback)
106+
callback(error);
107+
});
108+
109+
fstream.on('close', function() {
110+
if (callback)
111+
callback(null);
112+
});
113+
}
114+
};
115+
116+
// Non-array fields
117+
if (!req.files.hasOwnProperty(fieldname)) {
118+
req.files[fieldname] = newFile;
119+
} else {
120+
// Array fields
121+
if (req.files[fieldname] instanceof Array)
122+
req.files[fieldname].push(newFile);
123+
else
124+
req.files[fieldname] = [req.files[fieldname], newFile];
125+
}
126+
});
127+
});
128+
129+
busboy.on('finish', next);
130+
131+
req.pipe(busboy);
132+
}
133+
134+
135+
/**************************************************************
136+
* Methods below were copied from, or heavily inspired by
137+
* the Connect and connect-busboy packages
138+
**************************************************************/
139+
140+
/**
141+
* Ensures the request is not using a non-compliant multipart method
142+
* such as GET or HEAD
143+
* @param {Object} req Express req object
144+
* @return {Boolean}
145+
*/
146+
function hasAcceptableMethod(req) {
147+
return (UNACCEPTABLE_METHODS.indexOf(req.method) < 0);
148+
}
149+
150+
/**
151+
* Ensures that only multipart requests are processed by express-fileupload
152+
* @param {Object} req Express req object
153+
* @return {Boolean}
154+
*/
155+
function hasAcceptableMime(req) {
156+
var str = (req.headers['content-type'] || '').split(';')[0];
157+
158+
return ACCEPTABLE_MIME.test(str);
159+
}
160+
161+
/**
162+
* Ensures the request contains a content body
163+
* @param {Object} req Express req object
164+
* @return {Boolean}
165+
*/
166+
function hasBody(req) {
167+
return ('transfer-encoding' in req.headers) ||
168+
('content-length' in req.headers && req.headers['content-length'] !== '0');
169+
}

package.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
{
22
"name": "express-fileupload",
3-
"version": "0.0.7",
3+
"version": "0.1.0-beta",
44
"author": "Richard Girges <[email protected]>",
5-
"description": "Simple express file upload middleware that wraps around connect-busboy",
5+
"description": "Simple express file upload middleware that wraps around Busboy",
66
"main": "./lib/index",
77
"dependencies": {
88
"busboy": "^0.2.14",
9-
"connect-busboy": "0.0.2",
109
"fs-extra": "^0.22.1",
1110
"streamifier": "^0.1.1"
1211
},
@@ -20,7 +19,7 @@
2019
"forms",
2120
"multipart",
2221
"files",
23-
"connect-busboy",
22+
"busboy",
2423
"middleware"
2524
],
2625
"licenses": [

yarn.lock

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ brace-expansion@^1.0.0:
2424
balanced-match "^0.4.1"
2525
concat-map "0.0.1"
2626

27-
busboy@*, busboy@^0.2.14:
27+
busboy@^0.2.14:
2828
version "0.2.14"
2929
resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453"
3030
dependencies:
@@ -35,12 +35,6 @@ [email protected]:
3535
version "0.0.1"
3636
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
3737

38-
39-
version "0.0.2"
40-
resolved "https://registry.yarnpkg.com/connect-busboy/-/connect-busboy-0.0.2.tgz#ac5c9c96672171885e576c66b2bfd95d3bb11097"
41-
dependencies:
42-
busboy "*"
43-
4438
4539
version "0.5.2"
4640
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"

0 commit comments

Comments
 (0)