Archive

node.js HTML5 file uploader

I have been working on my node.js MVC CMS recently, and the last module missing to have all the core features was a file management system.

I was able to put together two nice plugins, clean it up little bit, and integrate in my JavaScript MVC paradigm.

For the node.js server side, I used formidable lib. For some reason, the part that was the biggest trouble was actually saving the file. Here is what the final solution looked like:

var formidable = require('formidable'),
		c = require('../../config'),
		fs = require('fs');

exports.upload = function(req, res){
	
	var form = new formidable.IncomingForm();
	
	// Parse file.
  form.parse(req, function(err, fields, files) {
  	
  	if(files.fileToUpload){
			
			// Read file.			
			fs.readFile(files.fileToUpload.path, function(err, data){

		  	// Save file.
				fs.writeFile(c.config.appPath + '/content/files/' + 
					files.fileToUpload.name, 
					data, 
					'utf8', 
					function (err) {
						if (err){
							// throw err;
							res.writeHead(200, {'content-type': 'text/plain'});
							res.write(JSON.stringify({
								isSucessful: false,
								message: 'Something went wrong!'					
							}));
							res.end();
						} else {
							// Sucess.
							res.writeHead(200, {'content-type': 'text/plain'});
							res.write(JSON.stringify({
								isSucessful: true,
								message: 'File was saved!'
							}));
							res.end();
						}
				});
			});
		} else {
			res.writeHead(200, {'content-type': 'text/plain'});
			res.write(JSON.stringify({
				isSucessful: false,
				message: 'Did not receive any file!'					
			}));
			res.end();
		}
  });
};

For the client, I found this tutorial about how to do an HTML5 AJAX uploader. After I cleaned up the code little bit, this is what it looks like:

var messageWrp = $('#fileDataMsg', content);
					
			// Source:
			// http://www.matlus.com/html5-file-upload-with-progress/
			
			$('#fileToUpload', content).change(function(){
				
        var file = document.getElementById('fileToUpload').files[0];
        
        if (file) {
          
          var fileSize = 0;
          
          if (file.size > 1024 * 1024){
            fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
          } else {
            fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';
					}
					
					// File info message.
					messageWrp.html('Name: ' + file.name + ' Size: ' + fileSize + ' Type: ' + file.type);
        }
      });

      $('#uploadFileBtn', content).click(function () {
      
      	// Form data.
        var fd = new FormData();
        fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]);
        
        var xhr = new XMLHttpRequest();
				
				// Progress listerner.
				xhr.upload.addEventListener("progress", function (evt) {
				  if (evt.lengthComputable) {
				    var percentComplete = Math.round(evt.loaded * 100 / evt.total);
				    messageWrp.html(percentComplete.toString() + '%');
				  }
				  else {
				    messageWrp.html('unable to compute');
				  }
				}, false);
				
				// On finished.
        xhr.addEventListener("load", function (evt) {
	      	
	      	// Parse json.
	      	var obj = $.parseJSON(evt.target.responseText);
	      
	      	window.location.hash = 'File';
	      
	        // Show success message.
	        MVC.message.show({text: obj.message, hideDealy: 2000});
	      }, false);
	      
	      // On failed.
        xhr.addEventListener("error", function (evt) {
	      	MVC.message.show({text: 'There was an error attempting to upload the file.', 
	      		hideDealy: 2000});
	      }, false);
        
        // On cancel.
        xhr.addEventListener("abort", function (evt) {
	      	MVC.message.show({text: 'The upload has been canceled by the user or the browser dropped the connection.', 
	      		hideDealy: 2000});
	      }, false);
	      
        xhr.open("POST", "/upload");
        xhr.send(fd);
        return false;
      });

Finally, this is the HTML part:

<form enctype="multipart/form-data" method="post" action="upload/">
<p>
<label for="fileToUpload">Select a File to Upload</label>
<input type="file" name="fileToUpload" id="fileToUpload" />
</p>
<p>
<a class="button" href="#" id="uploadFileBtn">Upload</a>
</p>
<p id="fileDataMsg"></p>
</form>

Comments: