Handle File Uploads in Express (Node.js)
How to handle file uploads in Express Node.js framework
Handling file uploads was a pain in Node.js some time ago. Then came formidable. It was a good start but uploading files was still not easy. Then came connect-form which made things a little easier, based on which I wrote a tutorial on handling POST requests in Express.
In the rapidly progressing Node.js landscape, it wasn't going to be long before the crucial file uploading functionality would become actually very easy. The time has come now - handling file uploads in very easy now in Express.
You no more require any extra module to work with file uploads in Express. File upload now works right out of the Express box. Along with req.body, now you have req.files which contains the details about the files you have uploaded.
If you don't want to specify the upload directory, you are all set to start processing file uploads without any additional changes to app.js. The files are uploaded to "/tmp" by default. If you want to make sure the files are uploaded to a specific directory, you change this
app.use(express.bodyParser());
to
app.use(express.bodyParser({uploadDir:'./uploads'}));
The express.bodyParser config option can found on line 16 as of Express v2.5.4. In the above example, the files will be uploaded to a directory named "uploads" in the same dir as the Express app.
To get a better idea about how the file upload and POST submission can be handled, let's create a form:
<form method="post" enctype="multipart/form-data" action="/file-upload">
<input type="text" name="username">
<input type="password" name="password">
<input type="file" name="thumbnail">
<input type="submit">
</form>
To handle the form submission, let's create a route:
app.post('/file-upload', function(req, res, next) {
console.log(req.body);
console.log(req.files);
});
It's that simple now!
In the above example, we can access the file from the req.files.thumbnail object. The object name is dependent what you set the name attribute to in your HTML form. In our case we set it as "thumbnail", so we access it from req.files.thumbnail.
A file object has these commonly useful properties:
size - size of the file in bytespath - the uploaded location of the file (files are renamed after uploading)name - original name of the file (as on your hard disk)type - the type of the file
To know the size of the file in our case: req.files.thumbnail.size
To know the original name of the file in our case: req.files.thumbnail.name
etc.
Note that the "uploads" directory is just the temporary upload directory; you will need to move the files to their respective intended locations once they are uploaded there. Following is an example of doing that. In this case we will be uploading the files to the "images" directory.
// we need the fs module for moving the uploaded files
var fs = require('fs');
app.post('/file-upload', function(req, res) {
// get the temporary location of the file
var tmp_path = req.files.thumbnail.path;
// set where the file should actually exists - in this case it is in the "images" directory
var target_path = './public/images/' + req.files.thumbnail.name;
// move the file from the temporary location to the intended location
fs.rename(tmp_path, target_path, function(err) {
if (err) throw err;
// delete the temporary file, so that the explicitly set temporary upload dir does not get filled with unwanted files
fs.unlink(tmp_path, function() {
if (err) throw err;
res.send('File uploaded to: ' + target_path + ' - ' + req.files.thumbnail.size + ' bytes');
});
});
};
You can set the upload destination to anywhere in the OS where you have access to. In case you want the files to be publicly available they should be uploaded within the "public" directory of Express. If want to upload the files to any sub-directory with "public", make sure they already exist else you will run into "Error: ENOENT, no such file" or directory error.
That's it! Handling file uploads in very easy in Express now. In case your old app is not handling file uploads as expected, try updating to the latest version of Express and NPM, and re-create the app. Any queries, suggestions; ping me in the comments.
that’s could be great! but it doesn’t work to me…
it say: TypeError: Cannot read property ‘thumbnail’ of undefined
and it give me undefined when I try console.log(req.files)…
this is my simple demo… can you give me an help please?
var express = require(‘express’);
var app = express.createServer();
app.use(express.bodyParser({uploadDir:’./’}));
app.configure( function () {
app.set(‘view engine’, ‘jade’);
app.set(‘views’, __dirname + ‘/views’);
app.use(app.router);
});
app.get(‘/’, function (req, res) {
res.render(‘upload’, {
title: ‘upload’
});
});
app.post(‘/’, function(req, res, next) {
console.log(req.body);
console.log(req.files);
res.redirect(‘back’);
});
app.listen(3000);
console.log(‘Server listening on port 3000′);
ok, sorry. I didn’t have the last release of express!
my fault!
now it works veeeery well!
thanks for your tutorials, very helpful!
Paolo
Great post, was struggling for too long until now.
However, when it comes to error handling like image size etc… , i’m not sure waht to do.
thanks.
Hi,
This sounds good and I tried it on simple demo as Paolo wrote above, but it didn’t work. I tried this with both of express v2.5.4 and the latest version, but it result the same. Did I miss installation of some module?
Thanks.
Tien
hi,
i am sorry, there is some mistakes in my test
, now it works so well
just install express v2.5.4 or later, by using this command below:
npm install express@2.5.4
Thank you so much for the helpful post
Tien.
@Yoav, if you can elaborate your problem, I can help you with it.
In this way,
app.use(express.bodyParser({uploadDir:’./uploads’}));
one Express server can only handle one file upload location? What if a web application needs to provide uploads of photos, video, articles, etc in different pages?
Hi etlolap, what you want to do is very easily done. I have updated the tutorial based on your feedback. Take a look at it.
If you are on Twitter, follow me @hacksparrow, I write excellent Node.js and related articles for beginners.
thank you dude, you have cleared the air of what to use to handle forms in Node + Express + some form library.
Most welcome, bro!
> if (err) throw err;
It is much better to define handler as function(req, res, next) and then do:
return next(err);
That way application won’t crash on error, but forward to error page instead.
Good idea, Vlad. One might want to do that in production. My code are just examples, feel free to improve it.
Hi,
Thanks for the tutorial.
Do you have any idea of how to go about listening for the file upload progress ? (for implementing a progress bar)
Thank you.
Hi Deepak, check this out – https://github.com/blueimp/jQuery-File-Upload
Hi
I am using express v2.5.8 – but when I use the example above, the req.files appears to be undefined. Is it tied to a specific version of connect or any other module (I’m using node v 0.6.10).
Thanks in advance
@Haider @Haider, make sure you are using the
bodyParsermiddleware. Locateapp.use(express.bodyParser());in app.js or add it.Thank You !!!!
Great tutorial. Makes it easy for us non-coders to get a grasp as well.
Quick question – how would you go about to give the uploaded file a unique name and capture the file extension, for instance tied to the user id of the user who uploaded the file. That would give you something that you could hook in to your templating. (we are using a concoction of angular, express, node and mongoose)
In our templating html I would like to do something like this to handled an uploaded file to serve as profile image:
Hi MIke,
Here is one way to go about that. Note that the code here is based on the example in the post.
// get the file extension
var a = req.files.thumbnail.name.split('.');
var ext = a.pop();
// let's create a unique file name
var random_name = req.session.username + '-' + +new Date() + Math.random();
// TIP: you might want to generate an md5 hash from `unique_name` (search my website)
var new_name = random_name +'.'+ ext;
var target_path = './public/images/' + new_name;
fs.rename(tmp_path, target_path, function(err) {
// yay!
});
That’s simple and easy, and works well. You have saved me many hours, thank you very much.
I wonder whether express can provide some kind of progress report too or not. In case of larger files that might be very handy, as currently the only response the user gets back is “connecting” until the file is stored fully in the file system. Once it’s stored, I might render another page, but not before.
Ivan, probably there are some modules / middlewares which so that. Implementing that should not be very tough.
hello Admin, i followed up the tuts and it realy help alot so thanx alot but i m facing this error
may i know what is the reason
TypeError: Cannot read property ‘path’ of undefined
I’d like to take the POST data from the form, and POST the image itself to another web service. Where does the actual byte stream for the uploaded image exist? Somewhere in req.files?
hi captain
i try this tutorial but have a mistake ,can you help me?
i use express3.0,and post a image.
get error:
Error: ENOENT, rename ‘/tmp/ae458b3e922927b04a006512c3c52d1c’
what this meaning?
Looks like a path issue.
hi
it would be great if u can post a tutorial about how to add progressbar using the jQuery-File-Upload in express.js.
do it man
is it possible to upload files in express.js without using file upload plugins, i think its possible,because nodejs handle the uploaded file as many pieces of data, so just send the uploaded size to the client side , but i think this means u should have a nonstoping connection,like socketio.can u give any advic
how to handle the multiple file uploading in nodejs.(if we want to upload more than one images what is the code to be used).
Thanks for this
Really helpful.
Actually I started using this script from my linux server and now I am getting this error too.
Error: ENOENT, rename ā/tmp/ae458b3e922927b04a006512c3c52d1cā
I know you said it was a path issue, but for the life of me I cannot resolve this.
@nithin
To handle multiple file uploading use the multiple attribute. This gives you the ability to select more than one file at a time.
label(for=’fileSelect’) Load File
input(type=’file’, name=’fileSelect’, multiple=’multiple’)
In your app.js, loop through your selected files:
var len = req.files.fileSelect.length;
for(var i = 0; i < len; i++) {
console.log(req.files.fileSelect[i]);
}
I too am getting the ENOENT error throw by the fs.rename() call. Adding a fs.existsSync() immediately before the rename shows that the file actually exists… What’s up with this?
Hi,
I am using express 3.0 and it seems that bodyParser is not working corectly. this is my threed in stack :
http://stackoverflow.com/questions/15086551/express-3-0-bodyparser-not-working-properly
@Kriss, looks like it was caused by an incompatible Node version. Glad you got it sorted out.
I’m getting the same error as Josh Kurz and loethen. You said it was a path issue. Any tips on how to fix it?
There is a bug in the fs.rename(). The error message reported by node is incorrect, it refers to the wrong argument! I forget the exact details, but if the error is saying that the source doesnt exist, i think it should actually say that the destination exists… hope that helps! checking the state of both src and dest arguments should reveal the problem anyway, don’t rely on node’s message.
hi im using your example and im getting the error that req.files is undefined.
i have the latest version of express and im literally just using that HTML form and the java script snip-it you posted. what do i do.
@ryan is enctype=”multipart/form-data” in place?