103

Please consider newer answers that have more up-to-date information as things have changed over the years!

Since many new Node.js libraries are quickly being rendered obsolete and there are relatively few examples anyways I want to ask about uploading images using:

  • Node.js (v0.4.1)
  • Express (1.0.7)
  • Mongoose (1.1.0).

How have others done it?

I've found: node-formidable, but I am new to uploading images in general so I want to learn general stuff and ways of doing so using Node.js and Express.

hong4rc
  • 3,999
  • 4
  • 21
  • 40
JohnAllen
  • 7,317
  • 9
  • 41
  • 65
  • 12
    **Update** Newer versions of Express has this functionality built in, consider that before spending time with 'connect-form' – user531694 Oct 21 '12 at 02:42
  • 4
    2015 tl;dr- send multipart/form requests to your server and parse them with Multer since BodyParser no longer parses files. `npm install multer --save` and then in your app you can access `req.files.your_file_param_name` and either save to s3 with `aws-sdk` or `fs.writeFile(...)` – user Apr 23 '15 at 05:21

13 Answers13

74

I'll answer my own question for the first time. I found an example straight from the source. Please forgive the poor indentation. I wasn't sure how to indent properly when copying and pasting. The code comes straight from Express multipart/form-data example on GitHub.

// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');

/**
 * Module dependencies.
 */

var express = require('../../lib/express')
  , form = require('connect-form');

var app = express.createServer(
  // connect-form (http://github.com/visionmedia/connect-form)
  // middleware uses the formidable middleware to parse urlencoded
  // and multipart form data
  form({ keepExtensions: true })
);

app.get('/', function(req, res){
  res.send('<form method="post" enctype="multipart/form-data">'
    + '<p>Image: <input type="file" name="image" /></p>'
    + '<p><input type="submit" value="Upload" /></p>'
    + '</form>');
});

app.post('/', function(req, res, next){

  // connect-form adds the req.form object
  // we can (optionally) define onComplete, passing
  // the exception (if any) fields parsed, and files parsed
  req.form.complete(function(err, fields, files){
    if (err) {
      next(err);
    } else {
      console.log('\nuploaded %s to %s'
        ,  files.image.filename
        , files.image.path);
      res.redirect('back');
    }
  });

  // We can add listeners for several form
  // events such as "progress"
  req.form.on('progress', function(bytesReceived, bytesExpected){
    var percent = (bytesReceived / bytesExpected * 100) | 0;
    process.stdout.write('Uploading: %' + percent + '\r');
  });
});

app.listen(3000);
console.log('Express app started on port 3000');
Rohan Kumar
  • 40,431
  • 11
  • 76
  • 106
JohnAllen
  • 7,317
  • 9
  • 41
  • 65
46

Since you're using express, just add bodyParser:

app.use(express.bodyParser());

then your route automatically has access to the uploaded file(s) in req.files:

app.post('/todo/create', function (req, res) {
    // TODO: move and rename the file using req.files.path & .name)
    res.send(console.dir(req.files));  // DEBUG: display available fields
});

If you name the input control "todo" like this (in Jade):

form(action="/todo/create", method="POST", enctype="multipart/form-data")
    input(type='file', name='todo')
    button(type='submit') New

Then the uploaded file is ready by the time you get the path and original filename in 'files.todo':

  • req.files.todo.path, and
  • req.files.todo.name

other useful req.files properties:

  • size (in bytes)
  • type (e.g., 'image/png')
  • lastModifiedate
  • _writeStream.encoding (e.g, 'binary')
Brad
  • 159,648
  • 54
  • 349
  • 530
Brent Faust
  • 9,103
  • 6
  • 53
  • 57
  • I've never heard it referred that way and I'm going to go ahead and say that these guys know best https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Working_with_Objects so I guess we are both wrong ;) – srquinn Apr 19 '13 at 00:17
  • `bodyParser` is insecure, at least according to this: http://andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html. [Jon J's answer](http://stackoverflow.com/a/22747216/560114) worked for me. – Matt Browne Apr 02 '14 at 20:02
  • By "insecure", it just means that temp files are created, so that an "attack" could possibly fill up the server's disk space with temp files. This is more a robustness issue, as it is not a security hole. – Brent Faust Apr 02 '14 at 23:03
19

You can configure the connect body parser middleware in a configuration block in your main application file:

    /** Form Handling */
    app.use(express.bodyParser({
        uploadDir: '/tmp/uploads',
        keepExtensions: true
    }))
    app.use(express.limit('5mb'));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
srquinn
  • 10,134
  • 2
  • 48
  • 54
  • This is actually the best way to handle uploads I suppose. Keep the file if you want to else simply delete it rather than copying it to a separate location. Thanks. – Eastern Monk Jul 30 '12 at 20:42
  • 2
    @AksharPrabhuDesai Yes and no. Lets say you have a photo upload/crop tool. If you were to allow the user to upload directly to your public folder, you have a serious security flaw. In this case, its best to upload to a tmp folder and then move into your public folder after confirming the file is not a Trojan. – srquinn Sep 24 '13 at 16:10
  • Doesn't seem to be supported anymore. Looked like a nice solution. – Blaze Dec 04 '15 at 13:11
14

See, the best thing you can do is to just upload the image to the disk and save the URL in MongoDB. Rest when you retrieve the image again. Just specify the URL, and you will get an image. The code for uploading is as follows.

app.post('/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.
    target_path = '/tmp/' + 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;
            //
        });
    });
});

Now save the target path in your MongoDB database.

Again, while retrieving the image, just extract the URL from the MongoDB database, and use it on this method.

fs.readFile(target_path, "binary", function(error, file) {
    if(error) {
        res.writeHead(500, {"Content-Type": "text/plain"});
        res.write(error + "\n");
        res.end();
    }
    else {
        res.writeHead(200, {"Content-Type": "image/png"});
        res.write(file, "binary");
    }
});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
log N
  • 925
  • 9
  • 33
9

Try this code.It will help.

app.get('/photos/new', function(req, res){
  res.send('<form method="post" enctype="multipart/form-data">'
    + '<p>Data: <input type="filename" name="filename" /></p>'
    + '<p>file: <input type="file" name="file" /></p>'
    + '<p><input type="submit" value="Upload" /></p>'
    + '</form>');
});


 app.post('/photos/new', function(req, res) {
  req.form.complete(function(err, fields, files) {
    if(err) {
      next(err);
    } else {
      ins = fs.createReadStream(files.photo.path);
      ous = fs.createWriteStream(__dirname + '/directory were u want to store image/' + files.photo.filename);
      util.pump(ins, ous, function(err) {
        if(err) {
          next(err);
        } else {
          res.redirect('/photos');
        }
      });
      //console.log('\nUploaded %s to %s', files.photo.filename, files.photo.path);
      //res.send('Uploaded ' + files.photo.filename + ' to ' + files.photo.path);
    }
  });
});

if (!module.parent) {
  app.listen(8000);
  console.log("Express server listening on port %d, log on to http://127.0.0.1:8000", app.address().port);
}
Mat
  • 202,337
  • 40
  • 393
  • 406
Dar Hamid
  • 1,949
  • 4
  • 21
  • 27
8

You can also use the following to set a path where it saves the file.

req.form.uploadDir = "<path>";
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Manuela
  • 116
  • 1
  • 1
5

I created an example that uses Express and Multer. It is very simple and avoids all Connect warnings

It might help somebody.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jon J
  • 491
  • 5
  • 8
  • 1
    Thanks for this. It's hard to find complete, up-to-date examples that don't use `bodyParser` (which is insecure, see http://andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html) – Matt Browne Apr 02 '14 at 20:01
2

For Express 3.0, if you want to use the formidable events, you must remove the multipart middleware, so you can create the new instance of it.

To do this:

app.use(express.bodyParser());

Can be written as:

app.use(express.json());
app.use(express.urlencoded());
app.use(express.multipart()); // Remove this line

And now create the form object:

exports.upload = function(req, res) {
    var form = new formidable.IncomingForm;
    form.keepExtensions = true;
    form.uploadDir = 'tmp/';

    form.parse(req, function(err, fields, files){
        if (err) return res.end('You found error');
        // Do something with files.image etc
        console.log(files.image);
    });

    form.on('progress', function(bytesReceived, bytesExpected) {
        console.log(bytesReceived + ' ' + bytesExpected);
    });

    form.on('error', function(err) {
        res.writeHead(400, {'content-type': 'text/plain'}); // 400: Bad Request
        res.end('error:\n\n'+util.inspect(err));
    });
    res.end('Done');
    return;
};

I have also posted this on my blog, Getting formidable form object in Express 3.0 on upload.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Risto Novik
  • 8,199
  • 9
  • 50
  • 66
2

Again if you don't want to use bodyParser, the following works:

var express = require('express');
var http = require('http');
var app = express();

app.use(express.static('./public'));


app.configure(function(){
    app.use(express.methodOverride());
    app.use(express.multipart({
        uploadDir: './uploads',
        keepExtensions: true
    }));
});


app.use(app.router);

app.get('/upload', function(req, res){
    // Render page with upload form
    res.render('upload');
});

app.post('/upload', function(req, res){
    // Returns json of uploaded file
    res.json(req.files);
});

http.createServer(app).listen(3000, function() {
    console.log('App started');
});
Squidinker
  • 169
  • 2
  • 14
1

I know that the original question related to specific versions, but it also referred to the "latest" - @JohnAllen 's post is no longer relevant due to Expressjs bodyParser and connect-form

This demonstrates the easy to use in-built bodyParser():

 /**
 * Module dependencies.
 */

var express = require('express')

var app = express()
app.use(express.bodyParser({ keepExtensions: true, uploadDir: '/home/svn/rest-api/uploaded' }))

app.get('/', function(req, res){
  res.send('<form method="post" enctype="multipart/form-data">'
    + '<p>Image: <input type="file" name="image" /></p>'
    + '<p><input type="submit" value="Upload" /></p>'
    + '</form>');
});

app.post('/', function(req, res, next){

    res.send('Uploaded: ' + req.files.image.name)
    return next()

});

app.listen(3000);
console.log('Express app started on port 3000');
Community
  • 1
  • 1
GMeister
  • 331
  • 2
  • 15
0

Here's a way to upload your images using the formidable package, which is recommended over bodyParser in later versions of Express. This also includes the ability to resize your images on the fly:

From my website: Uploading and Resizing Images (on the fly) With Node.js and Express.

Here's the gist:

var express = require("express"),
app = express(),
formidable = require('formidable'),
util = require('util')
fs   = require('fs-extra'),
qt   = require('quickthumb');

// Use quickthumb
app.use(qt.static(__dirname + '/'));

app.post('/upload', function (req, res){
  var form = new formidable.IncomingForm();
  form.parse(req, function(err, fields, files) {
    res.writeHead(200, {'content-type': 'text/plain'});
    res.write('received upload:\n\n');
    res.end(util.inspect({fields: fields, files: files}));
  });

  form.on('end', function(fields, files) {
    /* Temporary location of our uploaded file */
    var temp_path = this.openedFiles[0].path;
    /* The file name of the uploaded file */
    var file_name = this.openedFiles[0].name;
    /* Location where we want to copy the uploaded file */
    var new_location = 'uploads/';

    fs.copy(temp_path, new_location + file_name, function(err) {  
      if (err) {
        console.error(err);
      } else {
        console.log("success!")
      }
    });
  });
});

// Show the upload form 
app.get('/', function (req, res){
  res.writeHead(200, {'Content-Type': 'text/html' });
  /* Display the file upload form. */
  form = '<form action="/upload" enctype="multipart/form-data" method="post">'+ '<input name="title" type="text" />
  '+ '<input multiple="multiple" name="upload" type="file" />
  '+ '<input type="submit" value="Upload" />'+ '</form>';
  res.end(form); 
}); 
app.listen(8080);

NOTE: This requires Image Magick for the quick thumb resizing.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
0

There's my method to multiple upload file:

Nodejs :

router.post('/upload', function(req , res) {

var multiparty = require('multiparty');
var form = new multiparty.Form();
var fs = require('fs');

form.parse(req, function(err, fields, files) {  
    var imgArray = files.imatges;


    for (var i = 0; i < imgArray.length; i++) {
        var newPath = './public/uploads/'+fields.imgName+'/';
        var singleImg = imgArray[i];
        newPath+= singleImg.originalFilename;
        readAndWriteFile(singleImg, newPath);           
    }
    res.send("File uploaded to: " + newPath);

});

function readAndWriteFile(singleImg, newPath) {

        fs.readFile(singleImg.path , function(err,data) {
            fs.writeFile(newPath,data, function(err) {
                if (err) console.log('ERRRRRR!! :'+err);
                console.log('Fitxer: '+singleImg.originalFilename +' - '+ newPath);
            })
        })
}
})

Make sure your form has enctype="multipart/form-data"

I hope this gives you a hand ;)

Despertaweb
  • 1,672
  • 21
  • 29
0

It will become easy to store files after converting in string you just have to convert string in image in your frontend

convert image in to base64 string using this code in your api and also don't forgot to delete file from upload folder

"img": new Buffer.from(fs.readFileSync(req.file.path)).toString("base64")

to delete the file

       let resultHandler = function (err) {
            if (err) {
                console.log("unlink failed", err);
            } else {
                console.log("file deleted");
            }
        }

        fs.unlink(req.file.path, resultHandler);

at your routes import multer

 `multer const multer = require('multer');
  const upload = multer({ dest: __dirname + '/uploads/images' });`
  Add upload.single('img') in your request

  router.post('/fellows-details', authorize([Role.ADMIN, Role.USER]), 
        upload.single('img'), usersController.fellowsdetails);

OR

If you want save images in localstorage and want save path in database you can try following approach

you have to install first the fs-extra which will create folder. I am creating separate folders by id's if you want to remove it you can remove it. and to save path of image where it is uploaded add this code in your api or controller you are using to save image and and add it in database with other data

    let Id = req.body.id;
    let path = `tmp/daily_gasoline_report/${Id}`;

create separate folder for multer like multerHelper.js

 const multer = require('multer');
 let fs = require('fs-extra');

 let storage = multer.diskStorage({
 destination: function (req, file, cb) {
    let Id = req.body.id;
    let path = `tmp/daily_gasoline_report/${Id}`;
    fs.mkdirsSync(path);
    cb(null, path);
 },
 filename: function (req, file, cb) {
    // console.log(file);

  let extArray = file.mimetype.split("/");
  let extension = extArray[extArray.length - 1];
  cb(null, file.fieldname + '-' + Date.now() + "." + extension);
 }
})

let upload = multer({ storage: storage });

let createUserImage = upload.array('images', 100);

let multerHelper = {
     createUserImage,
}

   module.exports = multerHelper;

In your routes import multerhelper file

   const multerHelper = require("../helpers/multer_helper");

   router.post(multerHelper. createUserImage , function(req, res, next) {
      //Here accessing the body datas.
    })
Aryan
  • 3,338
  • 4
  • 18
  • 43