Завантаження зображень за допомогою Node.js, Express та Mongoose


102

Зверніть увагу на новіші відповіді, які містять найновішу інформацію, оскільки змінилися зміни з роками!

Оскільки багато нових бібліотек Node.js швидко стають застарілими і все одно є порівняно мало прикладів, я хочу запитати про завантаження зображень за допомогою:

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

Як це зробили інші?

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


12
Оновлення Новіші версії Express мають вбудовану функціональність, врахуйте, що перш ніж витрачати час на "connect-form"
user531694

4
2015 tl; dr- надсилайте на ваш сервер запити мультичастин / форм та аналізуйте їх за допомогою Multer, оскільки BodyParser більше не аналізує файли. npm install multer --saveа потім у вашому додатку ви можете отримати доступ req.files.your_file_param_nameта зберегти до s3 за допомогою aws-sdkабоfs.writeFile(...)
користувача

Відповіді:


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
Так, але як зберегти файл?
Нік Реталак

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

Це, мабуть, найкращий спосіб обробити завантаження. Зберігайте файл, якщо ви хочете інше, просто видаліть його, а не копіюйте його в окреме місце. Дякую.
Східний чернець

2
@AksharPrabhuDesai Так і ні. Скажімо, у вас є інструмент для завантаження / обрізання фотографій. Якщо ви повинні дозволити користувачу завантажуватися безпосередньо у вашу загальнодоступну папку, у вас є серйозний недолік безпеки. У цьому випадку найкраще завантажити в папку tmp і потім перейти у вашу загальнодоступну папку після підтвердження файлу - це не троянський.
srquinn

Схоже, це більше не підтримується. Виглядало як приємне рішення.
Блейз

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

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);зараз. Але чи видалить файл зображення зі старого місця розташування?
Еміель Ванденбусше

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

Я також опублікував це у своєму блозі, Отримавши грізний об’єкт форми в Експресі 3.0 при завантаженні .


Ваша пропозиція є оманливою, bodyParser в основному аналізує форму. і приймає грізні конфігураційні змінні.
Маріус

1
@timoxley це лише приклад
Risto Novik

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

0

Є мій спосіб множинного завантаження файлу:

Вузли:

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"

Я сподіваюся, що це допоможе вам;)


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

ПРИМІТКА. Для швидкого розміру великого пальця для цього потрібно Magic Image Magic.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.