e-Zest members share technology ideas to foster digital transformation.

How to Handle File Upload with Node, Express 4.0 and jQuery

Written by Nikhil Wanpal | Apr 7, 2015 3:55:16 PM

This post is a continuation of my previous post with a similar name, (except the added inputs on jQuery) and is a response to the feedback received for the first one. I have had tons of comments and questions for the previous post, thank you all for that. And this post is to answer the most recurrent question asked, as comments, as mails or even in person. So let us go for it. Prerequisite for this post is that you should have gone through the previous one and it would be great if you can have the code handy.

Can I do it without a page submit over AJAX? How?

Short answer is: Yes, you can. The long answer, well, it’s supposed to be long, so please read on.

First, let us see how it works. Files are sent over the network in multipart requests, chunks of data, which differ a little in the way other content like a url-encoded form data is sent. Multipart/form-data type requests which we used in the last post to upload the file, is a message with series of parts separated by boundaries. These appear on the server as a stream of data, something node is especially good at handling. And multer is a middleware that listens for the events triggered on the request data stream, identifies the file and form content, stores the uploaded files in a temporary folder and provides a handler to the content.

Browsers could do this with form submit, XMLHttpRequests couldn’t. Not till HTML5. Yes, this is to mean that the unfortunate of us who have to support older versions of IE are out of luck. You guys can stop reading now. The go to resource, caniuse shows that all modern browsers support it though.

What did XMLHttpRequest2 add? FormData interface. These objects allow creating multipart requests by manually adding the key-values or by directly feeding it an instance of the form DOM element. So much for the theory.

Here’s how to create a FormData object using an existing form:

var formData = new FormData($("#fileUpload")[0]); // FormData instance.

With the line above and little jQuery ajax code, lets look at the updated uploadPage.jade.

doctype html
html
	head
		title= title
		link(rel='stylesheet', href='/stylesheets/style.css')
		script( src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js")
	body
		block content
		h1= title
		form#fileUpload(method="post" action="/uploads/upload" enctype="multipart/form-data")
			label(for="payload") Select a file to upload:
			input#payload(type="file" name="myFile" accept="image/*")
			br
			button#upload Upload
		div
			span#message
script.
	$("#upload").click(function(event) {
		event.preventDefault();

		var formData = new FormData($("#fileUpload")[0]); // FormData instance.

		var messageEle = $("#message");

		$.ajax({
			url: "/uploads/upload",
			method: "POST",
			data: formData,
			processData: false,
			contentType: false,
			success: function(response, textStatus, jqXHR) {
				messageEle.text(response);
			},
			error: function(jqXHR, textStatus, errorThrown) {
				messageEle.text(errorThrown);
			}
		});
	});

That jQuery event binding is eating up the default click event of the button, which is submitting the form. Then it instantiates the FormData object with the actual form and triggers the AJAX request. The key thing to note here is the attributes processData and contentType. jQuery tries to convert the provided data into a query string, pass false to processData option tells it not to. jQuery adds a content type of ‘application/x-www-form-urlencoded’ by default. In this case, since we are sending the FormData object we need to tell it not to, hence the false.
That is it. It should just work, no change is required on the server side.

Let us consider the scenario where you do not have all the elements in the single form, or you are dynamically building the elements in the form, or you need to modify the elements before you send the request. In such a case, it is possible to construct the FormData manually.

var formData = new FormData($("#fileUpload")[0]);
formData.append(ele1);
formData.append(ele2);

The subsequent steps remain the same.

Hope this information helps. Would love to hear your feedback on this one as well. See you!