-
Notifications
You must be signed in to change notification settings - Fork 261
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
v0.1.0 Beta: No more urlencoded support. Multipart only. Removed conn…
…ect-busboy dependency in exchange for directly using Busboy. Updated README with breaking changes. This commit also fixes #11
- Loading branch information
1 parent
3d72084
commit 92d7ad5
Showing
4 changed files
with
169 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters