Przesyłanie obrazów za pomocą Node.js, Express i Mongoose

102

Proszę wziąć pod uwagę nowsze odpowiedzi, które zawierają bardziej aktualne informacje, ponieważ wiele się zmieniło na przestrzeni lat!

Ponieważ wiele nowych bibliotek Node.js szybko staje się przestarzałych, i tak jest stosunkowo niewiele przykładów, które chciałbym zapytać o przesyłanie obrazów za pomocą:

  • Node.js (wersja 0.4.1)
  • Express (1.0.7)
  • Mongoose (1.1.0).

Jak inni to zrobili?

Znalazłem: groźne dla węzłów , ale generalnie jestem nowy w przesyłaniu obrazów, więc chcę nauczyć się ogólnych rzeczy i sposobów robienia tego za pomocą Node.js i Express.

JohnAllen
źródło
12
Aktualizacja Nowsze wersje Express mają wbudowaną tę funkcję,
weź
4
2015 tl; dr- wysyłaj żądania wieloczęściowe / formularze na serwer i analizuj je za pomocą Multera, ponieważ BodyParser nie analizuje już plików. npm install multer --savea następnie w aplikacji możesz uzyskać dostęp req.files.your_file_param_namei zapisać do s3 za pomocą aws-sdklubfs.writeFile(...)
użytkownik

Odpowiedzi:

74

Odpowiem na swoje pytanie po raz pierwszy. Znalazłem przykład prosto ze źródła. Proszę wybacz biedne wcięcie. Nie byłem pewien, jak prawidłowo wciskać podczas kopiowania i wklejania. Kod pochodzi prosto z multipart/form-dataprzykładu Express na 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');
JohnAllen
źródło
3
Tak, ale jak zapisać plik?
Nick Retallack
1
@NickRetallack zapisany plik jest przechowywany pod adresem files.image.path
Robin Duckett
@ robin-duckett, w jaki sposób wcześniej określono nazwę pliku i ścieżkę?
Luc
4
@Luc: Nie, to jest zapisane w katalogu tymczasowym, z którego przenosisz go gdzie indziej.
kevmo314
1
W ten sposób konfigurujesz katalog wysyłania w express: // UWAGA: użyj bezwzględnej ścieżki do katalogu upload, aby uniknąć problemów w modułach podrzędnych! // app.use (express.bodyParser ({uploadDir: uploadDir}));
Risadinha
47

Ponieważ używasz express, po prostu dodaj bodyParser:

app.use(express.bodyParser());

Twoja trasa automatycznie uzyskuje dostęp do przesłanych plików w 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
});

Jeśli nazwiesz kontrolkę wejścia „todo” w ten sposób (w języku Jade):

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

Następnie przesłany plik jest gotowy do czasu uzyskania ścieżki i oryginalnej nazwy pliku w „files.todo”:

  • req.files.todo.path i
  • req.files.todo.name

inne przydatne właściwości wymagane plików:

  • rozmiar (w bajtach)
  • typ (np. „obraz / png”)
  • lastModifiedate
  • _writeStream.encoding (np. „binary”)
Brent Faust
źródło
Nigdy nie słyszałem tego w ten sposób i powiem, że ci faceci znają najlepiej developer.mozilla.org/en-US/docs/JavaScript/Guide/ ... więc myślę, że obaj się mylimy;)
srquinn
bodyParserjest niepewny, przynajmniej według tego: andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html . Odpowiedź Jona J. zadziałała dla mnie.
Matt Browne
Określenie „niezabezpieczone” oznacza po prostu, że tworzone są pliki tymczasowe, więc „atak” mógłby prawdopodobnie wypełnić miejsce na dysku serwera plikami tymczasowymi. Jest to bardziej kwestia solidności, ponieważ nie jest to luka w zabezpieczeniach.
Brent Faust
19

Oprogramowanie pośredniczące connect body parser można skonfigurować w bloku konfiguracyjnym w głównym pliku aplikacji:

    /** Form Handling */
    app.use(express.bodyParser({
        uploadDir: '/tmp/uploads',
        keepExtensions: true
    }))
    app.use(express.limit('5mb'));
srquinn
źródło
Przypuszczam, że jest to najlepszy sposób obsługi przesyłania. Zachowaj plik, jeśli chcesz go po prostu usunąć, zamiast kopiować do oddzielnej lokalizacji. Dzięki.
Eastern Monk,
2
@AksharPrabhuDesai Yes and no. Powiedzmy, że masz narzędzie do przesyłania / przycinania zdjęć. Jeśli pozwolisz użytkownikowi przesyłać pliki bezpośrednio do Twojego publicznego folderu, masz poważną lukę w zabezpieczeniach. W takim przypadku najlepiej jest przesłać plik do folderu tmp, a następnie przenieść do folderu publicznego po upewnieniu się, że plik nie jest trojanem.
srquinn,
Wydaje się, że nie jest już obsługiwany. Wyglądało na fajne rozwiązanie.
Blaze
14

Widzisz, najlepszą rzeczą, jaką możesz zrobić, jest po prostu załadowanie obrazu na dysk i zapisanie adresu URL w MongoDB. Odpocznij po ponownym odzyskaniu obrazu. Po prostu podaj adres URL, a otrzymasz obraz. Kod do wgrania jest następujący.

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

Teraz zapisz ścieżkę docelową w bazie danych MongoDB.

Ponownie, podczas pobierania obrazu, po prostu wyodrębnij adres URL z bazy danych MongoDB i użyj go w tej metodzie.

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");
    }
});
log N.
źródło
9

Wypróbuj ten kod, to pomoże.

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);
}
Dar Hamid
źródło
util.pump(ins, ous)jest amortyzowany, można to zrobić ins.pipe(ous);teraz. Ale czy spowoduje to usunięcie pliku obrazu w starej lokalizacji?
Emiel Vandenbussche,
8

Możesz również skorzystać z poniższych, aby ustawić ścieżkę, w której zapisuje plik.

req.form.uploadDir = "<path>";
Manuela
źródło
2

Ponownie, jeśli nie chcesz używać bodyParser, działa to:

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
źródło
2

W przypadku Express 3.0, jeśli chcesz używać potężnych zdarzeń, musisz usunąć wieloczęściowe oprogramowanie pośredniczące, aby móc utworzyć nowe jego wystąpienie.

Aby to zrobić:

app.use(express.bodyParser());

Można zapisać jako:

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

A teraz utwórz obiekt formularza:

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

Opublikowałem to również na moim blogu, Getting groźny obiekt formularza w Express 3.0 podczas przesyłania .

Risto Novik
źródło
Twoja sugestia jest myląca, bodyParser zasadniczo analizuje formularz. i akceptuje potężne zmienne konfiguracyjne.
Marius
1
@timoxley to tylko przykład
Risto Novik
1

Wiem, że pierwotne pytanie dotyczyło konkretnych wersji, ale odnosiło się również do "najnowszych" - post @JohnAllen nie jest już aktualny z powodu Expressjs bodyParser i connect-form

To pokazuje łatwy w użyciu wbudowany 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
źródło
0

Jest moja metoda na wielokrotne przesyłanie plików:

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

Upewnij się, że Twój formularz ma enctype = „multipart / form-data”

Mam nadzieję, że to ci pomoże;)

Despertaweb
źródło
0

Oto sposób przesyłania obrazów przy użyciu potężnego pakietu, który jest zalecany w przypadku bodyParser w późniejszych wersjach Express. Obejmuje to również możliwość zmiany rozmiaru obrazów w locie:

Z mojej witryny: przesyłanie i zmiana rozmiaru obrazów (w locie) za pomocą Node.js i Express .

Oto sedno:

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

UWAGA: Wymaga to Magii obrazu do szybkiej zmiany rozmiaru kciuka.

Tony Spiro
źródło