Posting text, XML, or JSON to the server is relatively easy, and most JavaScript libraries are optimized for that scenario.
Posting binary data is slightly trickier. Modern applications may need to be able to upload the generated binary files; examples include images drawn on an HTML5 canvas, ZIP files created with JSZip, and so on.
Additionally, it's convenient to be able to upload files selected using the HTML5 file API. We can do some interesting things with it, such as resumable file uploads by splitting the file into smaller parts and uploading every part separately to the server.
In this recipe, we're going to upload files selected by the user using a file input.
The server will be implemented using Node.js—you can download and install Node.js from http://nodejs.org/. The server will be implemented with the Node.js framework Connec t (http://www.senchalabs.org/connect/).
Let's write the client and server code.
index.html
—the file upload page that includes a file input, upload button, a progress bar, and a message container:<!DOCTYPE HTML> <html> <head> <title>Upload binary file</title> <style type="text/css"> .progress { position:relative; height:1em; width: 12em; border: solid 1px #aaa; } .progress div { position: absolute; top:0; bottom:0; left:0; background-color:#336699; } </style> </head> <body> <input type="file" id="file" value="Choose file"> <input type="button" id="upload" value="Upload"><br> <p id="info"></p> <div class="progress"><div id="progress"></div></div> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script> <script type="text/javascript" src="uploader.js"></script> <script type="text/javascript" src="example.js"></script> </body> </html>
uploader.js
that implements a binary file uploader. It posts the file to a specified URL and returns an object that enables the binding of progress events:window.postBinary = function(url, data) { var self = {}, xhr = new XMLHttpRequest(); xhr.open('POST', url, true); xhr.responseType = 'text'; self.done = function(cb) { xhr.addEventListener('load', function() { if (this.status == 200) cb(null, this.response) else cb(this.status, this.response) }); return self; } self.progress = function(cb) { xhr.upload.addEventListener('progress', function(e) { if (e.lengthComputable) cb(null, e.loaded / e.total); else cb('Progress not available'), }); return progress; }; xhr.send(data); return self; };
example.js
that uses the API provided by uploader.js
to add the upload functionality to the upload form:$(function() { var file; $("#file").on('change', function(e) { file = this.files[0] }); $("#upload").on('click', function() { $("#info").text("Uploading..."); $("#progress").css({width:0}); if (!file) { $("#info").text('No file selected') return; } var upload = postBinary('/upload/' + file.name, file); upload.progress(function(err, percent) { if (err) { $("#info").text(err); return; } $("#progress").css({width: percent + '%'}); }); upload.done(function(err, res) { if (err) { $("#info").text(err + ' ' + res); return; } $("#progress").css({width: '100%'}); $("#info").text("Upload complete"); }); }); });
server.js
—a Node.js server based on the Node.js Connect framework that handles the file uploads and serves the static files:var path = require('path'), connect = require('connect'), fs = require('fs'), connect() .use('/upload', function(req, res) { var file = fs.createWriteStream( path.join(__dirname, 'uploads', req.url)) req.pipe(file); req.on('end', function() { res.end("ok"); }); }) .use(connect.static(__dirname)) .listen(8080);
server.js
is located and type the following commands to create a directory for the uploads, install the connect library, and start the server:mkdir uploads npm install connect node server.js
http://localhost:8080
to test the example. All the created files (including server.js
) should be in the same directory.The new XMLHttpRequest
object in HTML5 has a send
method that supports more types of data. It can accept File
, Blob
, and ArrayBuffer
objects. We use this new functionality together with the HTML5 File API to upload the file selected by the user. You can find out more about this API in the Using file inputs on the client side recipe of Chapter 4, Using HTML5 Input Components.
The new API also provides an upload
object, which is of type XMLHttpRequestUpload
. It allows us to attach event listeners to monitor the upload progress. We use this functionality to show a progress bar for the upload.
The server accepts the uploads at '/upload'
and saves the files to the uploads
directory. Additionally, it serves the static files in the example directory.
18.117.93.0