Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit db0036ec7c58c59d100d49741f235504e5f5f176 @tj tj committed Jun 29, 2010
@@ -0,0 +1,3 @@
+[submodule "support/expresso"]
+ path = support/expresso
+ url = git://github.com/visionmedia/expresso.git
No changes.
@@ -0,0 +1,5 @@
+
+test:
+ @./support/expresso/bin/expresso
+
+.PHONY: test
No changes.
@@ -0,0 +1,12 @@
+
+/*!
+ * Connect - Multipart
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
+ * MIT Licensed
+ */
+
+module.exports = function multipart(){
+ return function(req, res, next){
+
+ };
+};
Submodule expresso added at 1d0a1b
@@ -0,0 +1 @@
+/test/tmp/*
@@ -0,0 +1,4 @@
+test:
+ @find test/{simple,integration}/test-*.js | xargs -n 1 -t node
+
+.PHONY: test
@@ -0,0 +1,157 @@
+# Formidable
+
+## Purpose
+
+A node.js module for dealing with web forms.
+
+## Features
+
+* Fast (~500mb/sec), non-buffering multipart parser
+* Automatically writing file uploads to disk
+* Low memory footprint
+* Graceful error handling
+* Very high test coverage
+
+### Todo
+
+* Limit buffer size for fields
+* Implement QuerystringParser the same way as MultipartParser
+
+## Installation
+
+Via [npm](http://github.com/isaacs/npm):
+
+ npm install formidable@latest
+
+Manually:
+
+ git clone git://github.com/felixge/node-formidable.git formidable
+ vim my.js
+ # var formidable = require('./formidable');
+
+Note: Formidable requires [gently](http://github.com/felixge/node-gently) to run the unit tests, but you won't need it for just using the library.
+
+## Example
+
+Parse an incoming file upload.
+
+ var formidable = require('formidable')
+ , http = require('http')
+ , sys = require('sys');
+
+ http.createServer(function(req, res) {
+ if (req.url == '/upload' && req.method.toLowerCase() == 'post') {
+ // parse a file upload
+ var form = new formidable.IncomingForm();
+ form.parse(req, function(fields, files) {
+ res.writeHead(200, {'content-type': 'text/plain'});
+ res.write('received upload:\n\n');
+ res.end(sys.inspect({fields: fields, files: files}));
+ });
+ return;
+ }
+
+ // show a file upload form
+ res.writeHead(200, {'content-type': 'text/html'});
+ res.end
+ ( '<form action="/upload" enctype="multipart/form-data" method="post">'
+ + '<input type="text" name="title"><br>'
+ + '<input type="file" name="upload" multiple="multiple"><br>'
+ + '<input type="submit" value="Upload">'
+ + '</form>'
+ );
+ });
+
+## API
+
+### formdiable.IncomingForm
+
+#### new formdiable.IncomingForm()
+
+Creates a new incoming form.
+
+#### incomingForm.encoding = 'utf-8'
+
+The encoding to use for incoming form fields.
+
+#### incomingForm.uploadDir = '/tmp'
+
+The directory for placing file uploads in. You can later on move them using `fs.rename()`.
+
+#### incomingForm.keepExtensions = false
+
+If you want the files written to `incomingForm.uploadDir` to include the extensions of the original files, set this property to `true`.
+
+#### incomingForm.type
+
+Either 'multipart' or 'urlencoded' depending on the incoming request.
+
+#### incomingForm.bytesReceived
+
+The amount of bytes received for this form so far.
+
+#### incomingForm.bytesExpected
+
+The expected number of bytes in this form.
+
+#### incomingForm.parse(request, [cb])
+
+Parses an incoming node.js `request` containing form data. If `cb` is provided, all fields an files are collected and passed to the callback:
+
+ incomingForm.parse(req, function(err, fields, files) {
+ // ...
+ });
+
+#### incomingForm.onPart(part)
+
+You may overwrite this method if you are interested in directly accessing the multipart stream. Doing so will disable any `'field'` / `'file'` events processing which would occur otherwise, making you fully responsible for handling the processing.
+
+ incomingForm.onPart = function(part) {
+ part.addListener('data', function() {
+ // ...
+ });
+ }
+
+If you want to use formidable to only handle certain parts for you, you can do so:
+
+ incomingForm.onPart = function(part) {
+ if (!part.filename) {
+ // let formidable handle all non-file parts
+ incomingForm.handlePart(part);
+ }
+ }
+
+Check the code in this method for further inspiration.
+
+#### Event: 'progress' (bytesReceived, bytesExpected)
+
+Emitted after each incoming chunk of data that has been parsed. Can be used to roll your own progress bar.
+
+#### Event: 'field' (name, value)
+
+Emitted whenever a field / value pair has been received.
+
+#### Event: 'file' (name, file)
+
+Emitted whenever a field / file pair has been received. `file` is a JS object with the following properties:
+
+ { path: 'the path in the uploadDir this file was written to'
+ , filename: 'the name this file had on the computer of the uploader'
+ , mime: 'the mime type specified by the user agent of the uploader'
+ }
+
+#### Event: 'error' (err)
+
+Emitted when there is an error processing the incoming form. A request that experiences an error is automatically paused, you will have to manually call `request.resume()` if you want the request to continue firing `'data'` events.
+
+#### Event: 'end' ()
+
+Emitted when the entire request has been received, and all contained files have finished flushing to disk. This is a great place for you to send your response.
+
+## License
+
+Formidable is licensed under the MIT license.
+
+## Credits
+
+* [Ryan Dahl](http://twitter.com/ryah) for his work on [http-parser](http://github.com/ry/http-parser) which heavily inspired multipart_parser.js
@@ -0,0 +1,66 @@
+require('../test/common');
+var multipartParser = require('formidable/multipart_parser')
+ , MultipartParser = multipartParser.MultipartParser
+ , parser = new MultipartParser()
+ , Buffer = require('buffer').Buffer
+ , boundary = '-----------------------------168072824752491622650073'
+ , mb = 100
+ , buffer = createMultipartBuffer(boundary, mb * 1024 * 1024)
+ , callbacks =
+ { partBegin: -1
+ , partEnd: -1
+ , headerField: -1
+ , headerValue: -1
+ , partData: -1
+ , end: -1
+ };
+
+
+parser.initWithBoundary(boundary);
+parser.onHeaderField = function() {
+ callbacks.headerField++;
+};
+
+parser.onHeaderValue = function() {
+ callbacks.headerValue++;
+};
+
+parser.onPartBegin = function() {
+ callbacks.partBegin++;
+};
+
+parser.onPartData = function() {
+ callbacks.partData++;
+};
+
+parser.onPartEnd = function() {
+ callbacks.partEnd++;
+};
+
+parser.onEnd = function() {
+ callbacks.end++;
+};
+
+var start = +new Date()
+ , nparsed = parser.write(buffer)
+ , duration = +new Date - start
+ , mbPerSec = (mb / (duration / 1000)).toFixed(2);
+
+p(mbPerSec+' mb/sec');
+
+assert.equal(nparsed, buffer.length);
+
+function createMultipartBuffer(boundary, size) {
+ var head =
+ '--'+boundary+'\r\n'
+ + 'content-disposition: form-data; name="field1"\r\n'
+ + '\r\n'
+ , tail = '\r\n--'+boundary+'--\r\n'
+ , buffer = new Buffer(size);
+
+ buffer.write(head, 'ascii', 0);
+ buffer.write(tail, 'ascii', buffer.length - tail.length);
+ return buffer;
+}
+
+assert.callbacks(callbacks);
@@ -0,0 +1,43 @@
+require('../test/common');
+var http = require('http')
+ , sys = require('sys')
+ , formidable = require('formidable')
+ , server;
+
+server = http.createServer(function(req, res) {
+ if (req.url == '/') {
+ res.writeHead(200, {'content-type': 'text/html'});
+ res.end
+ ( '<form action="/post" method="post">'
+ + '<input type="text" name="title"><br>'
+ + '<input type="text" name="data[foo][]"><br>'
+ + '<input type="submit" value="Submit">'
+ + '</form>'
+ )
+ } else if (req.url == '/post') {
+ var form = new formidable.IncomingForm()
+ , fields = [];
+
+ form
+ .addListener('error', function(err) {
+ res.writeHead(200, {'content-type': 'text/plain'});
+ res.end('error:\n\n'+sys.inspect(err));
+ })
+ .addListener('field', function(field, value) {
+ p([field, value]);
+ fields.push([field, value]);
+ })
+ .addListener('end', function() {
+ puts('-> post done');
+ res.writeHead(200, {'content-type': 'text/plain'});
+ res.end('received fields:\n\n '+sys.inspect(fields));
+ });
+ form.parse(req);
+ } else {
+ res.writeHead(404, {'content-type': 'text/plain'});
+ res.end('404');
+ }
+});
+server.listen(TEST_PORT);
+
+sys.puts('listening on http://localhost:'+TEST_PORT+'/');
@@ -0,0 +1,48 @@
+require('../test/common');
+var http = require('http')
+ , sys = require('sys')
+ , formidable = require('formidable')
+ , server;
+
+server = http.createServer(function(req, res) {
+ if (req.url == '/') {
+ res.writeHead(200, {'content-type': 'text/html'});
+ res.end
+ ( '<form action="/upload" enctype="multipart/form-data" method="post">'
+ + '<input type="text" name="title"><br>'
+ + '<input type="file" name="upload" multiple="multiple"><br>'
+ + '<input type="submit" value="Upload">'
+ + '</form>'
+ )
+ } else if (req.url == '/upload') {
+ var form = new formidable.IncomingForm()
+ , files = []
+ , fields = [];
+
+ form.uploadDir = TEST_TMP;
+
+ form
+ .addListener('field', function(field, value) {
+ p([field, value]);
+ fields.push([field, value]);
+ })
+ .addListener('file', function(field, file) {
+ p([field, file]);
+ files.push([field, file]);
+ })
+ .addListener('end', function() {
+ puts('-> upload done');
+ res.writeHead(200, {'content-type': 'text/plain'});
+ res.write('received fields:\n\n '+sys.inspect(fields));
+ res.write('\n\n');
+ res.end('received files:\n\n '+sys.inspect(files));
+ });
+ form.parse(req);
+ } else {
+ res.writeHead(404, {'content-type': 'text/plain'});
+ res.end('404');
+ }
+});
+server.listen(TEST_PORT);
+
+sys.puts('listening on http://localhost:'+TEST_PORT+'/');
@@ -0,0 +1 @@
+module.exports = require('./lib/formidable');
@@ -0,0 +1 @@
+exports.IncomingForm = require('./incoming_form').IncomingForm;
Oops, something went wrong.

0 comments on commit db0036e

Please sign in to comment.