Handling File Uploads with Node.js and Express.js

Whether you are building social platform or a simple website, sometimes you need to let users upload their own files. The typical scenario might be: uploading the user's photo for a profile page. With Node.js and express it is quite easy to create your own file upload service. In this post I will show you how. In the new folder run `npm init`, and create `package.json` as you usually do for every fresh Node project. Now let's install the dependencies:
  1. npm install --save express multer
multer is a module that will help us to deal with `multipart/form-data` - a special encoding that browsers are using to transfer files from user's computer to server. First let's create a tiny application to display an HTML form to a user and also to render the uploaded photos. Create a new file: main.js and enter the following code:
  1. const express = require('express');
  2.  
  3. const app = express();
  4.  
  5. app.use(express.static(`${__dirname}/public`));
  6.  
  7. app.listen(3000, () => {
  8. console.log('File Uploader is on 0.0.0.0:3000!');
  9. });
  10. <javascript>
  11.  
  12. This application doesn't do much: it simply serves static files from 'public' folder. Now let's create index.html with an upload form so that our user can pick select a file.
  13.  
  14. <html4strict>
  15. <!doctype html>
  16. <html lang="en">
  17. <head>
  18. <meta charset="utf-8">
  19. <title>File Upload</title>
  20. </head>
  21. <body>
  22.  
  23. <h1>Upload file</h1>
  24.  
  25. <form enctype="multipart/form-data" method="POST" action="upload">
  26. <input type="file" name="file">
  27. <button type="submit">Upload</button>
  28. </form>
  29.  
  30. </body>
  31. </html>
  32. </html4strict>
  33.  
  34. Start the server and point your browser to http://localhost:3000. You should see the form showing up on the screen. Notice that in the HTML code we added few attributes to the form. `enctype="multipart/form-data"` is essenctial for file uploads. `action="upload"` points to the route that will handle the upload. Let's build that route now.
  35.  
  36. To do that we will need a help of little node.js module called `multer`. It will de-code the multipart-encoded form and reconstruct it as a JSON object on the server side. This way you will have a quick and easy access to all the submitted fields as well as submitted files.
  37.  
  38. First, let's initialize `multer`. We will need to tell it where to put uploaded files and what is the name of the submitted form field that contains the file.
  39.  
  40. <javascript>
  41. const uploadPath = `${__dirname}/public/upload`;
  42. const formFieldName = 'file';
  43. const upload = multer({dest: uploadPath}).single(formFieldName);
Now we are ready to make `express` route:
  1. app.post('/upload', (req, res) => {
  2.  
  3. const onUploadDone = (err) => {
  4. if (err) {
  5. console.log(err.stack);
  6. res.send('Could not upload file');
  7. return;
  8. }
  9.  
  10. res.send('File has been uploaded');
  11. };
  12.  
  13. upload(req, res, onUploadDone);
  14. });
Notice, that inside of route handler we pass request and response objects to `upload()` - a function created by `multer`. This function will parse the request body and enrich `req` object with new data. Finally, there is another callback that is executed once the file is uploaded and saved. Here's how our app looks now:
  1. const express = require('express');
  2. const multer = require('multer');
  3.  
  4. const app = express();
  5.  
  6.  
  7. const uploadPath = `${__dirname}/public/upload`;
  8. const formFieldName = 'file';
  9. const upload = multer({dest: uploadPath}).single(formFieldName);
  10.  
  11. app.use(express.static(`${__dirname}/public`));
  12.  
  13.  
  14. app.post('/upload', (req, res) => {
  15.  
  16. const onUploadDone = (err) => {
  17. if (err) {
  18. console.log(err.stack);
  19. res.send('Could not upload file');
  20. return;
  21. }
  22.  
  23. res.send('File has been uploaded');
  24. };
  25.  
  26. upload(req, res, onUploadDone);
  27. });
  28.  
  29.  
  30. app.listen(3000, () => console.log('Listening on 0.0.0.0:3000!'));
Try running it. Select the file and upload it. If you did everything correctly you should see "File has been uploaded" after you submit the form. Let's see what's inside our upload folder. If you open it up, you'll see the file is indeed there, but it has some strange name. 32 meaningless characters without the extension. This is not a bug, but a feature of multer - this way it makes sure that file names don't clash. If two users upload file called me.jpg, multer will not overwrite one of the files, both will be save. Most of the time you will want to keep this behavior but sometimes you just want to keep the original file name. Here's how you can do it. We only need to change onUploadDone()function.
  1. app.post('/upload', (req, res) => {
  2.  
  3. const onUploadDone = (err) => {
  4. if (err) {
  5. console.log(err.stack);
  6. res.send('Could not upload file');
  7. return;
  8. }
  9.  
  10. const targetPath = `${uploadPath}/${req.file.originalname}`;
  11. fs.rename(req.file.path, targetPath, () => {
  12. res.send('File has been uploaded');
  13. });
  14. };
  15.  
  16. upload(req, res, onUploadDone);
  17. });
Notice that we used fs - standard node module, to rename the file. Make sure to require() it in your code. Now the uploaded file will have its original name. Happy uploading! Here's the code of finished program:
  1. const fs = require('fs');
  2. const express = require('express');
  3. const multer = require('multer');
  4.  
  5. const app = express();
  6.  
  7. const uploadPath = `${__dirname}/public/upload`;
  8. const formFieldName = 'file';
  9. const upload = multer({dest: uploadPath}).single(formFieldName);
  10.  
  11. app.use(express.static(`${__dirname}/public`));
  12.  
  13.  
  14. app.post('/upload', (req, res) => {
  15.  
  16. const onUploadDone = (err) => {
  17. if (err) {
  18. console.log(err.stack);
  19. res.send('Could not upload file');
  20. return;
  21. }
  22.  
  23. const targetPath = `${uploadPath}/${req.file.originalname}`;
  24. fs.rename(req.file.path, targetPath, () => {
  25. res.send('File has been uploaded');
  26. });
  27. };
  28.  
  29. upload(req, res, onUploadDone);
  30. });
  31.  
  32.  
  33. app.listen(3000, () => console.log('Listening on 0.0.0.0:3000!'));

Add new comment