Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

multipart: use multiparty instead of formidable

  • Loading branch information...
commit 296398a001d97fd0e8dafa622fc75c874a06c3d6 1 parent 65482ea
@tj tj authored
View
1  Makefile
@@ -10,6 +10,7 @@ test:
@NODE_ENV=test ./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--timeout 600 \
+ --bail \
$(TESTS)
docs: $(HTML)
View
37 examples/upload-stream.js
@@ -0,0 +1,37 @@
+
+/**
+ * Module dependencies.
+ */
+
+var connect = require('../');
+var fs = require('fs');
+
+connect()
+ .use(connect.bodyParser({ defer: true }))
+ .use(form)
+ .use(upload)
+ .listen(3000);
+
+function form(req, res, next) {
+ if ('GET' !== req.method) return next();
+ res.setHeader('Content-Type', 'text/html');
+ res.end('<form method="post" enctype="multipart/form-data">'
+ + '<input type="file" name="images" multiple="multiple" />'
+ + '<input type="submit" value="Upload" />'
+ + '</form>');
+}
+
+function upload(req, res, next) {
+ if ('POST' !== req.method) return next();
+
+ req.form.on('part', function(part){
+ // transfer to s3 etc
+ console.log('upload %s %s', part.name, part.filename);
+ var out = fs.createWriteStream('/tmp/' + part.filename);
+ part.pipe(out);
+ });
+
+ req.form.on('close', function(){
+ res.end('uploaded!');
+ });
+}
View
3  examples/upload.js
@@ -23,6 +23,7 @@ function form(req, res, next) {
function upload(req, res, next) {
if ('POST' !== req.method) return next();
req.files.images.forEach(function(file){
- console.log(' uploaded : %s %skb', file.name, file.size / 1024 | 0);
+ console.log(' uploaded : %s %skb : %s', file.originalFilename, file.size / 1024 | 0, file.path);
});
+ res.end('Thanks');
}
View
14 lib/middleware/json.js
@@ -14,14 +14,6 @@ var utils = require('../utils')
, _limit = require('./limit');
/**
- * noop middleware.
- */
-
-function noop(req, res, next) {
- next();
-}
-
-/**
* JSON:
*
* Parse JSON request bodies, providing the
@@ -31,7 +23,7 @@ function noop(req, res, next) {
*
* - `strict` when `false` anything `JSON.parse()` accepts will be parsed
* - `reviver` used as the second "reviver" argument for JSON.parse
- * - `limit` byte limit disabled by default
+ * - `limit` byte limit [1mb]
*
* @param {Object} options
* @return {Function}
@@ -42,9 +34,7 @@ exports = module.exports = function(options){
var options = options || {}
, strict = options.strict !== false;
- var limit = options.limit
- ? _limit(options.limit)
- : noop;
+ var limit = _limit(options.limit || '1mb');
return function json(req, res, next) {
if (req._body) return next();
View
36 lib/middleware/multipart.js
@@ -9,20 +9,12 @@
* Module dependencies.
*/
-var formidable = require('formidable')
+var multiparty = require('multiparty')
, _limit = require('./limit')
, utils = require('../utils')
, qs = require('qs');
/**
- * noop middleware.
- */
-
-function noop(req, res, next) {
- next();
-}
-
-/**
* Multipart:
*
* Parse multipart/form-data request bodies,
@@ -31,18 +23,18 @@ function noop(req, res, next) {
*
* Configuration:
*
- * The options passed are merged with [formidable](https://github.com/felixge/node-formidable)'s
- * `IncomingForm` object, allowing you to configure the upload directory,
+ * The options passed are merged with [multiparty](https://github.com/superjoe30/node-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(connect.multipart({ uploadDir: path }));
*
* Options:
*
- * - `limit` byte limit defaulting to none
- * - `defer` defers processing and exposes the Formidable form object as `req.form`.
+ * - `limit` byte limit defaulting to [100mb]
+ * - `defer` defers processing and exposes the multiparty form object as `req.form`.
* `next()` is called without waiting for the form's "end" event.
- * This option is useful if you need to bind to the "progress" event, for example.
+ * This option is useful if you need to bind to the "progress" or "part" events, for example.
*
* @param {Object} options
* @return {Function}
@@ -52,9 +44,7 @@ function noop(req, res, next) {
exports = module.exports = function(options){
options = options || {};
- var limit = options.limit
- ? _limit(options.limit)
- : noop;
+ var limit = _limit(options.limit || '100mb');
return function multipart(req, res, next) {
if (req._body) return next();
@@ -76,7 +66,7 @@ exports = module.exports = function(options){
limit(req, res, function(err){
if (err) return next(err);
- var form = new formidable.IncomingForm
+ var form = new multiparty.Form
, data = {}
, files = {}
, done;
@@ -99,9 +89,11 @@ exports = module.exports = function(options){
ondata(name, val, data);
});
- form.on('file', function(name, val){
- ondata(name, val, files);
- });
+ if (!options.defer) {
+ form.on('file', function(name, val){
+ ondata(name, val, files);
+ });
+ }
form.on('error', function(err){
if (!options.defer) {
@@ -111,7 +103,7 @@ exports = module.exports = function(options){
done = true;
});
- form.on('end', function(){
+ form.on('close', function(){
if (done) return;
try {
req.body = qs.parse(data);
View
6 lib/middleware/urlencoded.js
@@ -30,7 +30,7 @@ function noop(req, res, next) {
*
* Options:
*
- * - `limit` byte limit disabled by default
+ * - `limit` byte limit [1mb]
*
* @param {Object} options
* @return {Function}
@@ -40,9 +40,7 @@ function noop(req, res, next) {
exports = module.exports = function(options){
options = options || {};
- var limit = options.limit
- ? _limit(options.limit)
- : noop;
+ var limit = _limit(options.limit || '1mb');
return function urlencoded(req, res, next) {
if (req._body) return next();
View
6 package.json
@@ -13,7 +13,6 @@
"author": "TJ Holowaychuk <tj@vision-media.ca> (http://tjholowaychuk.com)",
"dependencies": {
"qs": "0.6.5",
- "formidable": "1.0.14",
"cookie-signature": "1.0.1",
"buffer-crc32": "0.2.1",
"cookie": "0.1.0",
@@ -23,7 +22,8 @@
"pause": "0.0.1",
"uid2": "0.0.2",
"debug": "*",
- "methods": "0.0.1"
+ "methods": "0.0.1",
+ "multiparty": "2.1.8"
},
"devDependencies": {
"should": "*",
@@ -36,7 +36,7 @@
"type": "MIT",
"url": "https://raw.github.com/senchalabs/connect/master/LICENSE"
}
- ],
+ ],
"main": "index",
"engines": {
"node": ">= 0.8.0"
View
18 test/bodyParser.js
@@ -65,9 +65,9 @@ describe('connect.bodyParser()', function(){
app.use(function(req, res){
assert('Tobi' == req.body.user.name);
- req.files.text.path.should.not.include('.txt');
- req.files.text.constructor.name.should.equal('File');
- res.end(req.files.text.name);
+ req.files.text.originalFilename.should.equal('foo.txt');
+ req.files.text.path.should.include('.txt');
+ res.end(req.files.text.originalFilename);
});
app.request()
@@ -88,7 +88,7 @@ describe('connect.bodyParser()', function(){
});
})
- it('should expose options to formidable', function(done){
+ it('should expose options to multiparty', function(done){
var app = connect();
app.use(connect.bodyParser({
@@ -98,7 +98,7 @@ describe('connect.bodyParser()', function(){
app.use(function(req, res){
assert('Tobi' == req.body.user.name);
assert(~req.files.text.path.indexOf('.txt'));
- res.end(req.files.text.name);
+ res.end(req.files.text.originalFilename);
});
app.request()
@@ -175,8 +175,8 @@ describe('connect.bodyParser()', function(){
app.use(function(req, res){
req.files.text.should.have.length(2);
- req.files.text[0].constructor.name.should.equal('File');
- req.files.text[1].constructor.name.should.equal('File');
+ assert(req.files.text[0]);
+ assert(req.files.text[1]);
res.end();
});
@@ -205,8 +205,8 @@ describe('connect.bodyParser()', function(){
app.use(function(req, res){
Object.keys(req.files.docs).should.have.length(2);
- req.files.docs.foo.name.should.equal('foo.txt');
- req.files.docs.bar.name.should.equal('bar.txt');
+ req.files.docs.foo.originalFilename.should.equal('foo.txt');
+ req.files.docs.bar.originalFilename.should.equal('bar.txt');
res.end();
});
View
18 test/multipart.js
@@ -53,7 +53,7 @@ describe('connect.multipart()', function(){
app.use(function(req, res){
assert('Tobi' == req.body.user.name);
- res.end(req.files.text.name);
+ res.end(req.files.text.originalFilename);
});
app.request()
@@ -74,7 +74,7 @@ describe('connect.multipart()', function(){
});
})
- it('should expose options to formidable', function(done){
+ it('should expose options to multiparty', function(done){
var app = connect();
app.use(connect.multipart({
@@ -84,7 +84,7 @@ describe('connect.multipart()', function(){
app.use(function(req, res){
assert('Tobi' == req.body.user.name);
assert(~req.files.text.path.indexOf('.txt'));
- res.end(req.files.text.name);
+ res.end(req.files.text.originalFilename);
});
app.request()
@@ -161,8 +161,8 @@ describe('connect.multipart()', function(){
app.use(function(req, res){
req.files.text.should.have.length(2);
- req.files.text[0].constructor.name.should.equal('File');
- req.files.text[1].constructor.name.should.equal('File');
+ assert(req.files.text[0]);
+ assert(req.files.text[1]);
res.end();
});
@@ -191,8 +191,8 @@ describe('connect.multipart()', function(){
app.use(function(req, res){
Object.keys(req.files.docs).should.have.length(2);
- req.files.docs.foo.name.should.equal('foo.txt');
- req.files.docs.bar.name.should.equal('bar.txt');
+ req.files.docs.foo.originalFilename.should.equal('foo.txt');
+ req.files.docs.bar.originalFilename.should.equal('bar.txt');
res.end();
});
@@ -224,7 +224,7 @@ describe('connect.multipart()', function(){
});
app.use(function(err, req, res, next){
- err.message.should.equal('parser error, 16 of 28 bytes parsed');
+ err.message.should.equal('Expected alphabetic character, received 61');
res.statusCode = err.status;
res.end('bad request');
});
@@ -271,7 +271,7 @@ describe('connect.multipart()', function(){
app.use(function(req, res){
JSON.stringify(req.body).should.equal("{}");
- req.form.on("end", function() {
+ req.form.on('close', function() {
res.end(JSON.stringify(req.body));
});
});
Please sign in to comment.
Something went wrong with that request. Please try again.