diff --git a/README.md b/README.md
index b22eeb9..8f14050 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,13 @@
# Description
Simple express middleware for uploading files.
+# Version 0.1.0 Breaking Change
+As of `v0.1.0`, there is NO MORE `application/x-www-form-urlencoded` SUPPORT!
+
+If you want to parse `urlencoded` requests, [use body-parser](https://github.com/expressjs/body-parser#bodyparserurlencodedoptions).
+
+Moving forward, express-fileupload is considered a "multipart" solution only.
+
# Install
```bash
# With NPM
@@ -113,10 +120,6 @@ Option | Acceptable Values | Details
--- | --- | ---
safeFileNames |
false
**(default)**true
- regex
| 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.
**Example #1 (strip slashes from file names):** `app.use(fileUpload({ safeFileNames: /\\/g }))`
**Example #2:** `app.use(fileUpload({ safeFileNames: true }))`
-# Known Bugs
-##### If you're using bodyParser middleware
-Add `app.use(fileUpload())` *AFTER* `app.use(bodyParser.json)` and any other `bodyParser` middlewares! This limitation will be investigated in an upcoming release.
-
# Help Wanted
Pull Requests are welcomed!
diff --git a/lib/index.js b/lib/index.js
index 5849af0..5bb8bd5 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,95 +1,169 @@
-var busboy = require('connect-busboy');
+var Busboy = require('busboy');
var fs = require('fs-extra');
var streamifier = require('streamifier');
+var ACCEPTABLE_MIME = /^(?:multipart\/.+)$/i;
+var UNACCEPTABLE_METHODS = [
+ 'GET',
+ 'HEAD'
+];
+
module.exports = function(options) {
options = options || {};
return function(req, res, next) {
- return busboy(options)(req, res, function() {
-
- // If no busboy req obj, then no uploads are taking place
- if (!req.busboy)
+ if (!hasBody(req) || !hasAcceptableMethod(req) || !hasAcceptableMime(req))
return next();
- req.files = null;
-
- req.busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated, encoding, mimetype) {
- req.body = req.body || {};
-
- var prev = req.body[fieldname];
-
- if (!prev)
- return req.body[fieldname] = val;
-
- if (Array.isArray(prev))
- return prev.push(val);
-
- req.body[fieldname] = [prev, val];
- });
-
- req.busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
- var buf = new Buffer(0);
- var safeFileNameRegex = /[^\w-]/g;
-
- file.on('data', function(data) {
- buf = Buffer.concat([buf, data]);
-
- if (options.debug)
- return console.log('Uploading %s -> %s', fieldname, filename);
- });
-
- file.on('end', function() {
- if (!req.files)
- req.files = {};
-
- // see: https://github.com/richardgirges/express-fileupload/issues/14
- // firefox uploads empty file in case of cache miss when f5ing page.
- // resulting in unexpected behavior. if there is no file data, the file is invalid.
- if(!buf.length)
- return;
-
- if (options.safeFileNames) {
- if (typeof options.safeFileNames === 'object')
- safeFileNameRegex = options.safeFileNames;
-
- filename = filename.replace(safeFileNameRegex, '');
- }
-
- var newFile = {
- name: filename,
- data: buf,
- encoding: encoding,
- mimetype: mimetype,
- mv: function(path, callback) {
- var fstream;
- fstream = fs.createWriteStream(path);
- streamifier.createReadStream(buf).pipe(fstream);
- fstream.on('error', function(error) {
- if (callback)
- callback(error);
- });
- fstream.on('close', function() {
- if (callback)
- callback(null);
- });
- }
- };
-
- if (!req.files.hasOwnProperty(fieldname)) {
- req.files[fieldname] = newFile;
- } else {
- if (req.files[fieldname] instanceof Array)
- req.files[fieldname].push(newFile);
- else
- req.files[fieldname] = [req.files[fieldname], newFile];
- }
- });
- });
-
- req.busboy.on('finish', next);
-
- req.pipe(req.busboy);
- });
+ processMultipart(options, req, res, next);
};
};
+
+
+/**
+ * Processes multipart request
+ * Builds a req.body object for fields
+ * Builds a req.files object for files
+ * @param {Object} options expressFileupload and Busboy options
+ * @param {Object} req Express request object
+ * @param {Object} res Express response object
+ * @param {Function} next Express next method
+ * @return {void}
+ */
+function processMultipart(options, req, res, next) {
+ var busboyOptions = {};
+ var busboy;
+
+ req.files = null;
+
+ // Build busboy config
+ for (var k in options) {
+ busboyOptions[k] = options[k];
+ }
+
+ // Attach request headers to busboy config
+ busboyOptions.headers = req.headers;
+
+ // Init busboy instance
+ busboy = new Busboy(busboyOptions);
+
+ // Build multipart req.body fields
+ busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated, encoding, mime) {
+ req.body = req.body || {};
+
+ var prev = req.body[fieldname];
+
+ if (!prev)
+ return req.body[fieldname] = val;
+
+ if (Array.isArray(prev))
+ return prev.push(val);
+
+ req.body[fieldname] = [prev, val];
+ });
+
+ // Build req.files fields
+ busboy.on('file', function(fieldname, file, filename, encoding, mime) {
+ var buf = new Buffer(0);
+ var safeFileNameRegex = /[^\w-]/g;
+
+ file.on('data', function(data) {
+ buf = Buffer.concat([buf, data]);
+
+ if (options.debug)
+ return console.log('Uploading %s -> %s', fieldname, filename);
+ });
+
+ file.on('end', function() {
+ if (!req.files)
+ req.files = {};
+
+ // see: https://github.com/richardgirges/express-fileupload/issues/14
+ // firefox uploads empty file in case of cache miss when f5ing page.
+ // resulting in unexpected behavior. if there is no file data, the file is invalid.
+ if(!buf.length)
+ return;
+
+ if (options.safeFileNames) {
+ if (typeof options.safeFileNames === 'object')
+ safeFileNameRegex = options.safeFileNames;
+
+ filename = filename.replace(safeFileNameRegex, '');
+ }
+
+ var newFile = {
+ name: filename,
+ data: buf,
+ encoding: encoding,
+ mimetype: mime,
+ mv: function(path, callback) {
+ var fstream = fs.createWriteStream(path);
+
+ streamifier.createReadStream(buf).pipe(fstream);
+
+ fstream.on('error', function(error) {
+ if (callback)
+ callback(error);
+ });
+
+ fstream.on('close', function() {
+ if (callback)
+ callback(null);
+ });
+ }
+ };
+
+ // Non-array fields
+ if (!req.files.hasOwnProperty(fieldname)) {
+ req.files[fieldname] = newFile;
+ } else {
+ // Array fields
+ if (req.files[fieldname] instanceof Array)
+ req.files[fieldname].push(newFile);
+ else
+ req.files[fieldname] = [req.files[fieldname], newFile];
+ }
+ });
+ });
+
+ busboy.on('finish', next);
+
+ req.pipe(busboy);
+}
+
+
+/**************************************************************
+ * Methods below were copied from, or heavily inspired by
+ * the Connect and connect-busboy packages
+ **************************************************************/
+
+/**
+ * Ensures the request is not using a non-compliant multipart method
+ * such as GET or HEAD
+ * @param {Object} req Express req object
+ * @return {Boolean}
+ */
+function hasAcceptableMethod(req) {
+ return (UNACCEPTABLE_METHODS.indexOf(req.method) < 0);
+}
+
+/**
+ * Ensures that only multipart requests are processed by express-fileupload
+ * @param {Object} req Express req object
+ * @return {Boolean}
+ */
+function hasAcceptableMime(req) {
+ var str = (req.headers['content-type'] || '').split(';')[0];
+
+ return ACCEPTABLE_MIME.test(str);
+}
+
+/**
+ * Ensures the request contains a content body
+ * @param {Object} req Express req object
+ * @return {Boolean}
+ */
+function hasBody(req) {
+ return ('transfer-encoding' in req.headers) ||
+ ('content-length' in req.headers && req.headers['content-length'] !== '0');
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index d27a84b..cacd4d7 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,11 @@
{
"name": "express-fileupload",
- "version": "0.0.7",
+ "version": "0.1.0-beta",
"author": "Richard Girges ",
- "description": "Simple express file upload middleware that wraps around connect-busboy",
+ "description": "Simple express file upload middleware that wraps around Busboy",
"main": "./lib/index",
"dependencies": {
"busboy": "^0.2.14",
- "connect-busboy": "0.0.2",
"fs-extra": "^0.22.1",
"streamifier": "^0.1.1"
},
@@ -20,7 +19,7 @@
"forms",
"multipart",
"files",
- "connect-busboy",
+ "busboy",
"middleware"
],
"licenses": [
diff --git a/yarn.lock b/yarn.lock
index ad68e56..fbcc366 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -24,7 +24,7 @@ brace-expansion@^1.0.0:
balanced-match "^0.4.1"
concat-map "0.0.1"
-busboy@*, busboy@^0.2.14:
+busboy@^0.2.14:
version "0.2.14"
resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453"
dependencies:
@@ -35,12 +35,6 @@ concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
-connect-busboy@0.0.2:
- version "0.0.2"
- resolved "https://registry.yarnpkg.com/connect-busboy/-/connect-busboy-0.0.2.tgz#ac5c9c96672171885e576c66b2bfd95d3bb11097"
- dependencies:
- busboy "*"
-
content-disposition@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"