Загрузка изображений с помощью Node.js, Express и Mongoose

Обратите внимание на новые ответы, которые содержат более свежую информацию, поскольку с годами все изменилось!

Поскольку многие новые библиотеки Node.js быстро становятся устаревшими, а примеров в любом случае относительно мало, я хочу спросить о загрузке изображений с помощью:

  • Node.js (v0.4.1)
  • Экспресс (1.0.7)
  • Мангуст (1.1.0).

Как это сделали другие?

Я обнаружил: грозный узел, но я новичок в загрузке изображений в целом, поэтому я хотите узнать общие сведения и способы сделать это с помощью Node.js и Express.


person JohnAllen    schedule 01.03.2011    source источник
comment
Обновление. В более новых версиях Express эта функция встроена. Учтите, что прежде чем тратить время на "форму подключения"   -  person user531694    schedule 21.10.2012
comment
2015 tl; dr - отправлять запросы multipart / form на ваш сервер и анализировать их с помощью Multer, поскольку BodyParser больше не анализирует файлы. npm install multer --save, а затем в своем приложении вы можете получить доступ к req.files.your_file_param_name и либо сохранить в s3 с помощью aws-sdk, либо fs.writeFile(...)   -  person user    schedule 23.04.2015


Ответы (13)


Я впервые отвечу на свой вопрос. Я нашел пример прямо из первоисточника. Пожалуйста, простите за плохой отступ. Я не знал, как правильно делать отступ при копировании и вставке. Код взят прямо из multipart/form-data example на 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');
person JohnAllen    schedule 01.03.2011
comment
Ага, но как сохранить файл? - person Nick Retallack; 05.10.2011
comment
@NickRetallack сохраненный файл хранится в files.image.path - person Robin Duckett; 20.10.2011
comment
@ robin-duckett, как вы заранее указали имя файла и путь? - person Luc; 22.10.2011
comment
@Luc: Нет, он сохраняется во временном каталоге, из которого вы перемещаете его в другое место. - person kevmo314; 23.10.2011
comment
Вот как вы настраиваете каталог загрузки в express: // ПРИМЕЧАНИЕ: используйте абсолютный путь к каталогу загрузки, чтобы избежать проблем в подмодулях! // app.use (express.bodyParser ({uploadDir: uploadDir})); - person Risadinha; 27.09.2012

Поскольку вы используете экспресс, просто добавьте bodyParser:

app.use(express.bodyParser());

тогда ваш маршрут автоматически получит доступ к загруженным файлам в 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
});

Если вы назовете элемент управления вводом «todo» следующим образом (в Jade):

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

Затем загруженный файл будет готов к тому времени, когда вы получите путь и исходное имя файла в 'files.todo':

  • req.files.todo.path и
  • req.files.todo.name

другие полезные свойства req.files:

  • размер (в байтах)
  • тип (например, 'image / png')
  • lastModifiedate
  • _writeStream.encoding (например, "двоичный")
person Brent Faust    schedule 20.03.2012
comment
Я никогда не слышал, чтобы это называли таким образом, и я собираюсь сказать, что эти ребята лучше всех знают developer.mozilla.org/en-US/docs/JavaScript/Guide/, поэтому я думаю, что мы оба ошибаемся;) - person srquinn; 19.04.2013
comment
bodyParser небезопасен, по крайней мере, согласно следующему: andrewkelley. me / post / do-not-use-bodyparser-with-express-js.html. Ответ Джона Дж. сработал для меня. - person Matt Browne; 03.04.2014
comment
Под небезопасностью это просто означает, что создаются временные файлы, так что атака может заполнить дисковое пространство сервера временными файлами. Это скорее проблема надежности, так как это не дыра в безопасности. - person Brent Faust; 03.04.2014

Вы можете настроить промежуточное ПО парсера тела соединения в блоке конфигурации в главном файле приложения:

    /** Form Handling */
    app.use(express.bodyParser({
        uploadDir: '/tmp/uploads',
        keepExtensions: true
    }))
    app.use(express.limit('5mb'));
person srquinn    schedule 15.05.2012
comment
Я полагаю, что на самом деле это лучший способ обрабатывать загрузки. Сохраните файл, если вы хотите просто удалить его, а не копировать в отдельное место. Спасибо. - person Eastern Monk; 31.07.2012
comment
@AksharPrabhuDesai Да и нет. Допустим, у вас есть инструмент для загрузки / кадрирования фотографий. Если бы вы разрешили пользователю загружать файлы непосредственно в вашу общую папку, у вас возникнет серьезная брешь в безопасности. В этом случае лучше всего загрузить в папку tmp, а затем переместить в общую папку после подтверждения, что файл не является трояном. - person srquinn; 24.09.2013
comment
Похоже, что больше не поддерживается. Выглядело как хорошее решение. - person Blaze; 04.12.2015

Видите ли, лучшее, что вы можете сделать, - это просто загрузить изображение на диск и сохранить URL-адрес в MongoDB. Отдыхайте, когда снова получите изображение. Просто укажите URL-адрес, и вы получите изображение. Код для загрузки следующий.

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

Теперь сохраните целевой путь в своей базе данных MongoDB.

Опять же, при получении изображения просто извлеките URL-адрес из базы данных MongoDB и используйте его в этом методе.

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");
    }
});
person log N    schedule 13.09.2012

Попробуйте этот код - он поможет.

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);
}
person Dar Hamid    schedule 10.11.2011
comment
util.pump(ins, ous) обесценивается, теперь это можно сделать с ins.pipe(ous);. Но удалит ли это изображение на старом месте? - person Emiel Vandenbussche; 11.01.2017

Вы также можете использовать следующее, чтобы указать путь, по которому будет сохранен файл.

req.form.uploadDir = "<path>";
person Manuela    schedule 17.01.2012

Я создал пример, в котором используются Express и Multer. Это очень просто и позволяет избежать всех предупреждений Connect.

Это может кому-нибудь помочь.

person Jon J    schedule 30.03.2014
comment
Спасибо за это. Трудно найти полные актуальные примеры, в которых не используется bodyParser (что небезопасно, см. andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html) - person Matt Browne; 03.04.2014

Опять же, если вы не хотите использовать bodyParser, работает следующее:

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');
});
person Squidinker    schedule 11.04.2014

Для Express 3.0, если вы хотите использовать грозные события, вы должны удалить составное промежуточное ПО, чтобы вы могли создать его новый экземпляр.

Сделать это:

app.use(express.bodyParser());

Можно записать как:

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

А теперь создайте объект формы:

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

Я также разместил это в своем блоге, Получение внушительного объекта формы в Express 3.0 при загрузке.

person Risto Novik    schedule 02.12.2012
comment
Ваше предложение ошибочно, bodyParser в основном анализирует форму. и принимает огромные переменные конфигурации. - person Marius; 02.02.2013
comment
@timoxley это просто пример - person Risto Novik; 01.03.2013

Я знаю, что исходный вопрос относился к конкретным версиям, но он также относился к "последним" - сообщение @JohnAllen больше не актуально из-за Expressjs bodyParser и connect-form

Это демонстрирует простой в использовании встроенный 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');
person GMeister    schedule 12.05.2014

Вот мой способ загрузки нескольких файлов:

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

Убедитесь, что в вашей форме есть enctype = "multipart / form-data"

Надеюсь, это поможет вам;)

person Despertaweb    schedule 11.10.2016

Вот способ загрузки изображений с помощью огромного пакета, который рекомендуется использовать вместо bodyParser в более поздних версиях Express. Это также включает в себя возможность изменять размер ваших изображений на лету:

С моего веб-сайта: Загрузка и изменение размера изображений (на лету) с помощью Node .js и Express.

Вот суть:

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

ПРИМЕЧАНИЕ. Для быстрого изменения размера большого пальца требуется Image Magick.

person Tony Spiro    schedule 07.07.2014

После преобразования в строку станет легко хранить файлы, вам просто нужно преобразовать строку в изображение в вашем интерфейсе

преобразовать изображение в строку base64, используя этот код в вашем API, а также не забыть удалить файл из папки загрузки

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

удалить файл

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

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

на ваших маршрутах импортный мультер

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

ИЛИ

Если вы хотите сохранять изображения в localstorage и хотите сохранить путь в базе данных, вы можете попробовать следующий подход

вы должны сначала установить fs-extra, который создаст папку. Я создаю отдельные папки по идентификатору, если вы хотите удалить его, вы можете удалить его. и чтобы сохранить путь к изображению, куда оно загружено, добавьте этот код в свой api или контроллер, который вы используете для сохранения изображения, и добавьте его в базу данных с другими данными.

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

создать отдельную папку для multer, например 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;

В ваши маршруты импортируйте multerhelper файл

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

   router.post(multerHelper. createUserImage , function(req, res, next) {
      //Here accessing the body datas.
    })
person Arya    schedule 09.10.2020