使用 Node.js、 Express 和 Mongoose 上传图像

请考虑更新的答案,有更多的最新信息,因为事情已经改变了多年!

由于许多新的 Node.js 库很快就会过时,而且我想问的关于上传图片的例子也相对较少:

  • Node.js (v0.4.1)
  • Express (1.0.7)
  • 猫鼬(1.1.0)。

其他人是怎么做到的?

我发现: 节点强大,但我是新的上传图片一般,所以我想学习一般的东西和方法,这样做使用 Node.js 和 Express。

133051 次浏览

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');

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);
}

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

req.form.uploadDir = "<path>";

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')

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'));

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");
}
});

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.

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

It might help somebody.

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');
});

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');

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.

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 ;)

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.
})