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

102

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

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

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

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

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

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

Ответы:

74

Я отвечу на свой вопрос впервые. Я нашел пример прямо из первоисточника. Пожалуйста, простите за плохой отступ. Я не знал, как правильно делать отступ при копировании и вставке. Код взят прямо из multipart/form-dataпримера Express на 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');
Джон Аллен
источник
3
Ага, но как сохранить файл?
Ник Реталлак 05
1
@NickRetallack, сохраненный файл хранится в files.image.path
Робин Дакетт,
@ robin-duckett, как вы заранее указали имя файла и путь?
Люк,
4
@Luc: Нет, он сохраняется во временном каталоге, из которого вы перемещаете его в другое место.
kevmo314
1
Вот как вы настраиваете каталог загрузки в express: // ПРИМЕЧАНИЕ: используйте абсолютный путь к каталогу загрузки, чтобы избежать проблем в подмодулях! // app.use (express.bodyParser ({uploadDir: uploadDir}));
Risadinha,
47

Поскольку вы используете экспресс, просто добавьте 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 (например, "двоичный")
Брент Фауст
источник
Я никогда не слышал, чтобы это называли таким образом, и я собираюсь пойти дальше и сказать, что эти ребята знают лучше developer.mozilla.org/en-US/docs/JavaScript/Guide/… так что я думаю, что мы оба ошибаемся;)
srquinn
bodyParserнебезопасно, по крайней мере, согласно этому: andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html . Ответ Джона Дж. Сработал для меня.
Мэтт Браун
Под «небезопасным» это просто означает, что создаются временные файлы, так что «атака» может заполнить дисковое пространство сервера временными файлами. Это скорее вопрос надежности, так как это не дыра в безопасности.
Brent Faust
19

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

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

Видите ли, лучшее, что вы можете сделать, - это просто загрузить изображение на диск и сохранить 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");
    }
});
войти N
источник
9

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

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);
}
Дар Хамид
источник
util.pump(ins, ous)обесценивается, это можно сделать ins.pipe(ous);сейчас. Но удалит ли это файл изображения в старом месте?
Emiel
8

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

req.form.uploadDir = "<path>";
Мануэла
источник
5

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

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

Джон Дж.
источник
1
Спасибо за это. Трудно найти полные актуальные примеры, которые не используются bodyParser(что небезопасно, см. Andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html )
Мэтт Браун,
2

Опять же, если вы не хотите использовать 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');
});
Кальмар
источник
2

Для 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 при загрузке .

Ристо Новик
источник
Ваше предложение ошибочно, bodyParser в основном анализирует форму. и принимает огромные переменные конфигурации.
Мариус
1
@timoxley, это всего лишь пример
Ристо Новик
1

Я знаю, что исходный вопрос относился к конкретным версиям, но он также относился к "последним" - сообщение @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');
GMeister
источник
0

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

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"

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

Despertaweb
источник
0

Вот способ загрузки изображений с помощью огромного пакета, который рекомендуется использовать вместо 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.

Тони Спиро
источник