Permalink
Browse files

Parse file uploads properly

  • Loading branch information...
1 parent a7dce03 commit 792503a3b29a206e8529a97047be0d3cd709a0f0 @felixge committed May 18, 2010
Showing with 159 additions and 2 deletions.
  1. +52 −2 lib/formidable/incoming_form.js
  2. +1 −0 test/common.js
  3. +106 −0 test/simple/test-incoming-form.js
@@ -1,4 +1,6 @@
var sys = require('sys')
+ , fs = require('fs')
+ , path = require('path')
, MultipartParser = require('./multipart_parser').MultipartParser
, Utf8Decoder = require('utf8decoder').Utf8Decoder
, events = require('events');
@@ -8,6 +10,7 @@ var IncomingForm = exports.IncomingForm = function() {
this.hadError = false;
+ this.uploadDir = '/tmp';
this.encoding = 'utf-8';
this.headers = null;
this.type = null;
@@ -24,6 +27,11 @@ IncomingForm.prototype.parse = function(req, cb) {
return true;
};
+ this.resume = function() {
+ req.resume();
+ return true;
+ };
+
this.writeHeaders(req.headers);
var self = this;
@@ -62,11 +70,18 @@ IncomingForm.prototype.pause = function() {
return false;
};
+IncomingForm.prototype.resume = function() {
+ // this does nothing, unless overwritten in IncomingForm.parse
+ return false;
+};
+
+
IncomingForm.prototype.onPart = function(part) {
+ var self = this;
+
if (!part.filename) {
var value = ''
- , decoder
- , self = this;
+ , decoder;
if (this.encoding.toLowerCase() == 'utf-8') {
decoder = new Utf8Decoder();
@@ -88,7 +103,29 @@ IncomingForm.prototype.onPart = function(part) {
self.emit('field', part.name, value);
}
});
+ return;
}
+
+ var file = this._writeStream(this._uploadPath());
+ part.addListener('data', function(buffer) {
+ self.pause();
+ file.write(buffer, function() {
+ self.resume();
+ });
+ });
+
+ part.addListener('end', function() {
+ file.end(function() {
+ self.emit
+ ( 'file'
+ , part.name
+ , { path: file.path
+ , filename: part.filename
+ , mime: part.mime
+ }
+ );
+ });
+ });
};
IncomingForm.prototype._parseContentType = function() {
@@ -208,4 +245,17 @@ IncomingForm.prototype._initMultipart = function(boundary) {
IncomingForm.prototype._initUrlencoded = function() {
this.type = 'urlencoded';
+};
+
+IncomingForm.prototype._uploadPath = function() {
+ var name = '';
+ for (i = 0; i < 32; i++) {
@aheckmann
aheckmann Nov 4, 2010

Global variable leaking here. Should be for (var i = 0 ...

@felixge
felixge Nov 12, 2010 owner

Thanks! Fixed in 82050b7

+ name += Math.floor(Math.random() * 16).toString(16);
+ }
+
+ return path.join(this.uploadDir, name);
+};
+
+IncomingForm.prototype._writeStream = function(path) {
+ return new fs.WriteStream(path);
};
View
@@ -10,6 +10,7 @@ global.p = sys.p;
global.assert = require('assert');
global.TEST_PORT = 13532;
global.TEST_FIXTURES = path.join(__dirname, 'fixture');
+global.TEST_TMP = path.join(__dirname, 'tmp');
assert.timeout = function(ms) {
return setTimeout(function() {
@@ -1,6 +1,8 @@
require('../common');
var formidable = require('formidable')
, events = require('events')
+ , fs = require('fs')
+ , path = require('path')
, Buffer = require('buffer').Buffer
, fixtures = require('../fixture/multipart')
, MultipartParser = require('formidable/multipart_parser').MultipartParser
@@ -17,6 +19,7 @@ var formidable = require('formidable')
[ 'hadError'
, 'type'
, 'headers'
+ , 'uploadDir'
, 'encoding'
, 'bytesTotal'
, 'bytesReceived'
@@ -66,6 +69,16 @@ var formidable = require('formidable')
assert.equal(pauseCalled, 1);
})();
+ (function testResume() {
+ var resumeCalled = 0;
+ reqMock.resume = function() {
+ resumeCalled++;
+ };
+
+ assert.strictEqual(form.resume(), true);
+ assert.equal(resumeCalled, 1);
+ })();
+
(function testEmitError() {
var ERR = new Error('something bad happened')
, errorCalled = 0;
@@ -99,6 +112,12 @@ var formidable = require('formidable')
assert.strictEqual(form.pause(), false);
})();
+(function testResume() {
+ var form = new formidable.IncomingForm();
+ assert.strictEqual(form.resume(), false);
+})();
+
+
(function testWriteHeaders() {
var HEADERS = {}
, form = new formidable.IncomingForm()
@@ -461,4 +480,91 @@ var formidable = require('formidable')
assert.equal(emitCalled, 1);
})();
+ (function testFilePart() {
+ var PART = new events.EventEmitter()
+ , FILE = new events.EventEmitter();
+
+ PART.name = 'my_file';
+ PART.filename = 'sweet.txt';
+ PART.mime = 'sweet.txt';
+
+ FILE.path = form._uploadPath();
+
+ var writeStreamCalled = 0;
+ form._writeStream = function(file) {
+ assert.equal(path.dirname(file), form.uploadDir);
+ writeStreamCalled++;
+ return FILE;
+ };
+
+ form.onPart(PART);
+ assert.equal(writeStreamCalled, 1);
+
+ var writeCalled = 0, BUFFER;
+ FILE.write = function(buffer, cb) {
+ writeCalled++;
+ assert.strictEqual(buffer, BUFFER);
+
+ var resumeCalled = 0;
+ form.resume = function() {
+ resumeCalled++;
+ };
+ cb();
+ assert.equal(resumeCalled, 1);
+ };
+
+ var pauseCalled = 0;
+ form.pause = function() {
+ pauseCalled++;
+ };
+
+ PART.emit('data', BUFFER = new Buffer('test'));
+ assert.equal(writeCalled, 1);
+ assert.equal(pauseCalled, 1);
+
+ var endCalled = 0;
+ FILE.end = function(cb) {
+ endCalled++;
+
+ var emitCalled = 0;
+ form.emit = function(event, field, file) {
+ emitCalled++;
+ assert.equal(event, 'file');
+ assert.deepEqual
+ ( file
+ , { path: FILE.path
+ , filename: PART.filename
+ , mime: PART.mime
+ }
+ );
+ };
+ cb();
+ assert.equal(emitCalled, 1);
+ };
+ PART.emit('end');
+ assert.equal(endCalled, 1);
+ })();
+})();
+
+(function testUploadPath() {
+ var form = new formidable.IncomingForm()
+ , path1 = form._uploadPath()
+ , path2 = form._uploadPath();
+
+ assert.equal(form.uploadDir, '/tmp');
+
+ assert.equal(path.dirname(path1), form.uploadDir);
+ assert.equal(path.dirname(path2), form.uploadDir);
+ assert.notEqual(path1, path2);
+})();
+
+(function testWriteStream() {
+ var form = new formidable.IncomingForm();
+ form.uploadDir = TEST_TMP;
+
+ var file = form._writeStream(form._uploadPath());
+ assert.ok(file instanceof fs.WriteStream);
+ file.end(function() {
+ fs.unlink(file.path);
+ });
})();

0 comments on commit 792503a

Please sign in to comment.