Cargar imágenes con Node.js, Express y Mongoose

102

¡Considere las respuestas más nuevas que tienen información más actualizada ya que las cosas han cambiado a lo largo de los años!

Dado que muchas bibliotecas nuevas de Node.js se están volviendo obsoletas rápidamente y hay relativamente pocos ejemplos de todos modos, quiero preguntar sobre la carga de imágenes usando:

  • Node.js (v0.4.1)
  • Expreso (1.0.7)
  • Mangosta (1.1.0).

¿Cómo lo han hecho otros?

He encontrado: node-formidable , pero soy nuevo en la carga de imágenes en general, así que quiero aprender cosas generales y formas de hacerlo usando Node.js y Express.

John Allen
fuente
12
Actualización Las versiones más nuevas de Express tienen esta funcionalidad incorporada, considere eso antes de pasar tiempo con 'connect-form'
user531694
4
2015 tl; dr: envíe solicitudes de formularios / múltiples partes a su servidor y analícelas con Multer, ya que BodyParser ya no analiza archivos. npm install multer --savey luego en su aplicación puede acceder req.files.your_file_param_namey guardar en s3 con aws-sdkofs.writeFile(...)
usuario

Respuestas:

74

Responderé a mi propia pregunta por primera vez. Encontré un ejemplo directamente de la fuente. Por favor, perdone la mala sangría. No estaba seguro de cómo sangrar correctamente al copiar y pegar. El código proviene directamente del multipart/form-dataejemplo Express en 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');
John Allen
fuente
3
Sí, pero ¿cómo se guarda el archivo?
Nick Retallack
1
@NickRetallack el archivo guardado se almacena en files.image.path
Robin Duckett
@ robin-duckett, ¿cómo especificó el nombre de archivo y la ruta de antemano?
Luc
4
@Luc: No lo hace, se guarda en un directorio temporal desde el que lo mueve a otro lugar.
kevmo314
1
Así es como se configura el directorio de carga en express: // NOTA: ¡use una ruta absoluta al directorio de carga para evitar problemas en los submódulos! // app.use (express.bodyParser ({uploadDir: uploadDir}));
Risadinha
47

Ya que está usando express, simplemente agregue bodyParser:

app.use(express.bodyParser());

entonces su ruta automáticamente tiene acceso a los archivos cargados en 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
});

Si nombra el control de entrada "todo" así (en Jade):

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

Luego, el archivo cargado estará listo cuando obtenga la ruta y el nombre del archivo original en 'files.todo':

  • req.files.todo.path, y
  • req.files.todo.name

otras propiedades útiles de los archivos requeridos:

  • tamaño (en bytes)
  • tipo (por ejemplo, 'imagen / png')
  • lastModifiedate
  • _writeStream.encoding (p. ej., 'binario')
Brent Faust
fuente
Nunca escuché que se refiriera de esa manera y voy a seguir adelante y decir que estos chicos conocen mejor a developer.mozilla.org/en-US/docs/JavaScript/Guide/… así que supongo que ambos estamos equivocados;)
srquinn
bodyParseres inseguro, al menos de acuerdo con esto: andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html . La respuesta de Jon J funcionó para mí.
Matt Browne
Por "inseguro", simplemente significa que se crean archivos temporales, de modo que un "ataque" posiblemente podría llenar el espacio en disco del servidor con archivos temporales. Este es más un problema de robustez, ya que no es un agujero de seguridad.
Brent Faust
19

Puede configurar el middleware del analizador de cuerpo de conexión en un bloque de configuración en su archivo de aplicación principal:

    /** Form Handling */
    app.use(express.bodyParser({
        uploadDir: '/tmp/uploads',
        keepExtensions: true
    }))
    app.use(express.limit('5mb'));
srquinn
fuente
Esta es en realidad la mejor manera de manejar las cargas, supongo. Conserve el archivo si lo desea, simplemente elimínelo en lugar de copiarlo en una ubicación separada. Gracias.
Eastern Monk
2
@AksharPrabhuDesai Sí y no. Supongamos que tiene una herramienta para cargar / recortar fotos. Si permitiera que el usuario cargara directamente en su carpeta pública, tiene una falla de seguridad grave. En este caso, es mejor cargarlo en una carpeta tmp y luego pasar a su carpeta pública después de confirmar que el archivo no es un troyano.
srquinn
Parece que ya no tiene soporte. Parecía una buena solución.
Blaze
14

Mira, lo mejor que puedes hacer es simplemente subir la imagen al disco y guardar la URL en MongoDB. Descanse cuando recupere la imagen nuevamente. Simplemente especifique la URL y obtendrá una imagen. El código para cargar es el siguiente.

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

Ahora guarde la ruta de destino en su base de datos MongoDB.

Nuevamente, mientras recupera la imagen, simplemente extraiga la URL de la base de datos MongoDB y úsela en este método.

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
fuente
9

Prueba este código, te ayudará.

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
fuente
util.pump(ins, ous)está depreciado, esto podría hacerse ins.pipe(ous);ahora. ¿Pero esto eliminará el archivo de imagen en la ubicación anterior?
Emiel Vandenbussche
8

También puede utilizar lo siguiente para establecer una ruta donde guardar el archivo.

req.form.uploadDir = "<path>";
Manuela
fuente
2

Nuevamente, si no desea utilizar bodyParser, lo siguiente funciona:

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');
});
Calamar
fuente
2

Para Express 3.0, si desea utilizar los eventos formidables, debe eliminar el middleware multiparte para poder crear la nueva instancia del mismo.

Para hacer esto:

app.use(express.bodyParser());

Se puede escribir como:

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

Y ahora crea el objeto de formulario:

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

También publiqué esto en mi blog, Obteniendo un formidable objeto de formulario en Express 3.0 al cargar .

Risto Novik
fuente
Su sugerencia es engañosa, bodyParser básicamente analiza el formulario. y acepta formidables variables de configuración.
Marius
1
@timoxley esto es solo un ejemplo
Risto Novik
1

Sé que la pregunta original estaba relacionada con versiones específicas, pero también se refería a la "última": la publicación de @JohnAllen ya no es relevante debido a Expressjs bodyParser y connect-form

Esto demuestra el bodyParser () incorporado fácil de usar:

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

Ahí está mi método para cargar varios archivos:

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

Asegúrese de que su formulario tenga enctype = "multipart / form-data"

Espero que esto te eche una mano;)

Despertaweb
fuente
0

Aquí hay una forma de cargar sus imágenes utilizando el formidable paquete, que se recomienda sobre bodyParser en versiones posteriores de Express. Esto también incluye la capacidad de cambiar el tamaño de sus imágenes sobre la marcha:

Desde mi sitio web: Carga y cambio de tamaño de imágenes (sobre la marcha) con Node.js y Express .

Aquí está la esencia:

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: Esto requiere Image Magick para cambiar el tamaño del pulgar rápidamente.

Tony Spiro
fuente