Caricamento di immagini utilizzando Node.js, Express e Mongoose


102

Si prega di prendere in considerazione le risposte più recenti che contengono informazioni più aggiornate poiché le cose sono cambiate nel corso degli anni!

Poiché molte nuove librerie Node.js vengono rapidamente rese obsolete e ci sono comunque relativamente pochi esempi che voglio chiedere sul caricamento di immagini utilizzando:

  • Node.js (v0.4.1)
  • Express (1.0.7)
  • Mangusta (1.1.0).

Come l'hanno fatto gli altri?

Ho trovato: node-formidable , ma sono nuovo nel caricare immagini in generale, quindi voglio imparare cose generali e modi per farlo usando Node.js ed Express.


12
Aggiorna Le versioni più recenti di Express hanno questa funzionalità incorporata, considera che prima di dedicare del tempo a 'connect-form'
user531694

4
2015 tl; dr- invia richieste multipart / form al tuo server e le analizza con Multer poiché BodyParser non analizza più i file. npm install multer --savequindi nella tua app puoi accedere req.files.your_file_param_namee salvare su s3 con aws-sdkofs.writeFile(...)
utente

Risposte:


74

Risponderò alla mia domanda per la prima volta. Ho trovato un esempio direttamente dalla fonte. Per favore perdona la scarsa rientranza. Non ero sicuro di come rientrare correttamente durante la copia e incolla. Il codice viene direttamente dalla espresso multipart/form-dataesempio su 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
Già ma come si salva il file?
Nick Retallack

1
@NickRetallack il file salvato è archiviato in files.image.path
Robin Duckett

@ robin-duckett, come hai specificato in anticipo il nome del file e il percorso?
Luc

4
@ Luc: Non lo fai, viene salvato in una directory temporanea da cui lo sposti altrove.
kevmo314

1
Ecco come configurare la directory di caricamento in express: // NOTA: utilizzare un percorso assoluto per la directory di caricamento per evitare problemi nei sottomoduli! // app.use (express.bodyParser ({uploadDir: uploadDir}));
Risadinha,

47

Dato che stai usando Express, aggiungi semplicemente bodyParser:

app.use(express.bodyParser());

quindi il tuo percorso ha automaticamente accesso ai file caricati 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
});

Se chiami il controllo di input "todo" in questo modo (in Jade):

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

Quindi il file caricato è pronto quando ottieni il percorso e il nome del file originale in 'files.todo':

  • req.files.todo.path e
  • req.files.todo.name

altre utili proprietà req.files:

  • dimensione (in byte)
  • tipo (ad es. "immagine / png")
  • lastModifiedate
  • _writeStream.encoding (ad esempio, 'binary')

Non l'ho mai sentito riferirsi in questo modo e andrò avanti dicendo che questi ragazzi conoscono meglio developer.mozilla.org/en-US/docs/JavaScript/Guide/… quindi immagino che ci sbagliamo entrambi;)
srquinn

bodyParserè insicuro, almeno secondo questo: andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html . La risposta di Jon J ha funzionato per me.
Matt Browne

Con "insicuro", significa semplicemente che vengono creati file temporanei, in modo che un "attacco" possa riempire lo spazio su disco del server con file temporanei. Questo è più un problema di robustezza, in quanto non è un buco di sicurezza.
Brent Faust

19

È possibile configurare il middleware del parser del corpo di connessione in un blocco di configurazione nel file dell'applicazione principale:

    /** Form Handling */
    app.use(express.bodyParser({
        uploadDir: '/tmp/uploads',
        keepExtensions: true
    }))
    app.use(express.limit('5mb'));

Questo è in realtà il modo migliore per gestire i caricamenti suppongo. Conservare il file se si desidera eliminarlo semplicemente anziché copiarlo in una posizione separata. Grazie.
Monaco orientale

2
@AksharPrabhuDesai Sì e no. Diciamo che hai uno strumento di caricamento / ritaglio di foto. Se dovessi consentire all'utente di caricare direttamente nella tua cartella pubblica, hai un grave difetto di sicurezza. In questo caso, è meglio caricare in una cartella tmp e poi spostarsi nella cartella pubblica dopo aver verificato che il file non sia un Trojan.
srquinn

Non sembra più essere supportato. Sembrava una bella soluzione.
Blaze

14

Vedi, la cosa migliore che puoi fare è semplicemente caricare l'immagine sul disco e salvare l'URL in MongoDB. Riposati quando recuperi di nuovo l'immagine. Basta specificare l'URL e otterrai un'immagine. Il codice per il caricamento è il seguente.

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

Ora salva il percorso di destinazione nel tuo database MongoDB.

Anche in questo caso, durante il recupero dell'immagine, è sufficiente estrarre l'URL dal database MongoDB e utilizzarlo con questo metodo.

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

Prova questo codice, ti aiuterà.

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)è ammortizzato, questo potrebbe essere fatto ins.pipe(ous);ora. Ma questo rimuoverà il file immagine nella vecchia posizione?
Emiel Vandenbussche

8

È inoltre possibile utilizzare quanto segue per impostare un percorso in cui salvare il file.

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


2

Ancora una volta, se non vuoi usare bodyParser, funziona quanto segue:

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

Per Express 3.0, se si desidera utilizzare eventi formidabili, è necessario rimuovere il middleware multiparte, in modo da poter creare la nuova istanza di esso.

Per fare questo:

app.use(express.bodyParser());

Può essere scritto come:

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

E ora crea l'oggetto modulo:

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

Ho anche pubblicato questo sul mio blog, Ottenere un formidabile oggetto modulo in Express 3.0 al caricamento .


Il tuo suggerimento è fuorviante, bodyParser analizza fondamentalmente il modulo. e accetta variabili di configurazione formidabili.
Marius

1
@timoxley questo è solo un esempio
Risto Novik

1

So che la domanda originale riguardava versioni specifiche, ma si riferiva anche all '"ultima" - il post di @JohnAllen non è più rilevante a causa di Expressjs bodyParser e connect-form

Questo dimostra il bodyParser () integrato di facile utilizzo:

 /**
 * 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

C'è il mio metodo per caricare più 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);
            })
        })
}
})

Assicurati che il tuo modulo abbia enctype = "multipart / form-data"

Spero che questo ti dia una mano;)


0

Ecco un modo per caricare le tue immagini utilizzando il formidabile pacchetto, consigliato su bodyParser nelle versioni successive di Express. Ciò include anche la possibilità di ridimensionare le immagini al volo:

Dal mio sito web: caricamento e ridimensionamento delle immagini (al volo) con Node.js ed Express .

Ecco il succo:

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

NOTA: questo richiede Image Magick per il ridimensionamento rapido del pollice.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.