Update: I just updated the code so it works with node v0.1.18.
Not excited by hello world in node.js? No problem.
Let's say you are a startup focusing on upload technology and you want the maximum level of control for your file uploads. In our case that means having the ability to directly interact with the multipart data stream as it comes in (so we can abort the upload if something isn't right, - beats the hell out of letting the user wait an hour to tell him after the upload has finished).
Here is a complete example on how to accomplish this in node.js (you'll need the bleeding edge git version):
var http = require('http');
var multipart = require('multipart');
var sys = require('sys');
var server = http.createServer(function(req, res) {
switch (req.uri.path) {
case '/':
display_form(req, res);
break;
case '/upload':
upload_file(req, res);
break;
default:
show_404(req, res);
break;
}
});
server.listen(8000);
function display_form(req, res) {
res.sendHeader(200, {'Content-Type': 'text/html'});
res.sendBody(
'<form action="/upload" method="post" enctype="multipart/form-data">'+
'<input type="file" name="upload-file">'+
'<input type="submit" value="Upload">'+
'</form>'
);
res.finish();
}
function upload_file(req, res) {
req.setBodyEncoding('binary');
var stream = new multipart.Stream(req);
stream.addListener('part', function(part) {
part.addListener('body', function(chunk) {
var progress = (stream.bytesReceived / stream.bytesTotal * 100).toFixed(2);
var mb = (stream.bytesTotal / 1024 / 1024).toFixed(1);
sys.print("Uploading "+mb+"mb ("+progress+"%)\015");
// chunk could be appended to a file if the uploaded file needs to be saved
});
});
stream.addListener('complete', function() {
res.sendHeader(200, {'Content-Type': 'text/plain'});
res.sendBody('Thanks for playing!');
res.finish();
sys.puts("\n=> Done");
});
}
function show_404(req, res) {
res.sendHeader(404, {'Content-Type': 'text/plain'});
res.sendBody('You r doing it rong!');
res.finish();
}
The code is rather straight forward. First of all we include the multipart.js parser which is a library that has just been added to node.js.
Next we create a server listening on port 8000 that dispatches incoming requests to one of our 3 functions: display_form, upload_file or show_404. Again, very straight forward.
display_form serves a very short (and invalid) piece of HTML that will render a file upload form with a submit button. You can get to it by running the example (via: node uploader.js) and pointing your browser to http://localhost:8000/.
upload_file kicks in as soon as you select a file and hit the submit button. It tells the request object to expect binary data and then passes the work on to the multipart Stream parser. The result is a new stream object that emits two kinds of events: 'part' and 'complete'. 'part' is called whenever a new element is found within the multipart stream, you can find all the information about it by looking at the first argument's headers property. In order to get the actual contents of this part we attach a 'body' listener to it, which gets called for each chunk of bytes getting uploaded. In our example we just use this event to render a progress indicator in our command line, but we could also append this chunk to a file which would eventually become the entire file as uploaded from the browser. Finally the 'complete' event sends a response to the browser indicating the file has been uploaded.
show_404 is handling all unknown urls by returning an error response.
As you can see, the entire process is pretty simple, yet gives you a ton of control. You can also easily use this technique to show an AJAX progress bar for the upload to your users. The multipart parser also works with non-HTTP requests, just pass it the {boundary: '...'} option into the constructor and use steam.write() to pass it some data to parse. Check out the source of the parser if you're curious how it works internally.
-- Felix Geisendörfer aka the_undefined