Upload de imagens usando Node.js, Express e Mongoose


102

Considere as respostas mais recentes com informações mais atualizadas, pois as coisas mudaram ao longo dos anos!

Uma vez que muitas novas bibliotecas Node.js estão rapidamente se tornando obsoletas e existem relativamente poucos exemplos de qualquer maneira, eu gostaria de perguntar sobre o upload de imagens usando:

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

Como os outros fizeram isso?

Eu descobri: node formidável , mas sou novo no upload de imagens em geral, então quero aprender coisas gerais e maneiras de fazer isso usando Node.js e Express.


12
Atualizar Versões mais recentes do Express têm essa funcionalidade incorporada, considere que antes de gastar tempo com o 'formulário de conexão'
user531694,

4
2015 tl; dr- envia solicitações multipart / form para o seu servidor e analisa-as com o Multer, já que o BodyParser não analisa mais os arquivos. npm install multer --savee, em seu aplicativo, você pode acessar req.files.your_file_param_namee salvar em s3 com aws-sdkoufs.writeFile(...)
usuário de

Respostas:


74

Vou responder minha própria pergunta pela primeira vez. Eu encontrei um exemplo direto da fonte. Por favor, perdoe o pobre recuo. Eu não tinha certeza de como recuar corretamente ao copiar e colar. O código vem direto do Expressmultipart/form-data exemplo no 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
Sim, mas como você salva o arquivo?
Nick Retallack,

1
@NickRetallack o arquivo salvo está armazenado em files.image.path
Robin Duckett

@ robin-duckett, como você especificou o nome do arquivo e o caminho de antemão?
Luc,

4
@Luc: Você não precisa, ele é salvo em um diretório temporário do qual você o move para outro lugar.
kevmo314

1
É assim que você configura o diretório de upload em express: // NOTA: use um caminho absoluto para o diretório de upload para evitar problemas em submódulos! // app.use (express.bodyParser ({uploadDir: uploadDir}));
Risadinha

47

Como você está usando express, basta adicionar bodyParser:

app.use(express.bodyParser());

então sua rota terá acesso automático ao (s) arquivo (s) carregado (s) em 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 você nomear o controle de entrada como "todo" assim (em Jade):

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

Então, o arquivo enviado estará pronto quando você obtiver o caminho e o nome do arquivo original em 'files.todo':

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

outras propriedades req.files úteis:

  • tamanho (em bytes)
  • tipo (por exemplo, 'imagem / png')
  • lastModifiedate
  • _writeStream.encoding (por exemplo, 'binário')

Eu nunca ouvi isso ser referido dessa forma e vou prosseguir e dizer que esses caras conhecem melhor o developer.mozilla.org/en-US/docs/JavaScript/Guide/… então acho que estamos ambos errados;)
srquinn

bodyParseré inseguro, pelo menos de acordo com isso: andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html . A resposta de Jon J funcionou para mim.
Matt Browne,

Por "inseguro", significa apenas que os arquivos temporários são criados, de forma que um "ataque" poderia preencher o espaço em disco do servidor com arquivos temporários. Esta é mais uma questão de robustez, pois não é uma falha de segurança.
Brent Faust,

19

Você pode configurar o middleware do analisador de corpo de conexão em um bloco de configuração no arquivo principal do aplicativo:

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

Essa é realmente a melhor maneira de lidar com uploads, suponho. Guarde o arquivo se quiser simplesmente excluí-lo em vez de copiá-lo para um local separado. Obrigado.
Monge Oriental

2
@AksharPrabhuDesai Sim e não. Digamos que você tenha uma ferramenta de upload / corte de fotos. Se você permitir que o usuário carregue diretamente em sua pasta pública, você terá uma falha de segurança séria. Nesse caso, é melhor fazer upload para uma pasta tmp e mover para a pasta pública após confirmar que o arquivo não é um cavalo de Tróia.
srquinn

Não parece mais ser compatível. Parecia uma boa solução.
Blaze

14

Veja, a melhor coisa que você pode fazer é apenas enviar a imagem para o disco e salvar a URL no MongoDB. Descanse ao recuperar a imagem novamente. Basta especificar o URL e você obterá uma imagem. O código para upload é o seguinte.

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

Agora salve o caminho de destino em seu banco de dados MongoDB.

Novamente, ao recuperar a imagem, apenas extraia a URL do banco de dados MongoDB e use-a neste 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");
    }
});

9

Tente este código. Ele ajudará.

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)está depreciado, isso poderia ser feito ins.pipe(ous);agora. Mas isso removerá o arquivo de imagem do local antigo?
Emiel Vandenbussche

8

Você também pode usar o seguinte para definir um caminho onde o arquivo será salvo.

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


2

Novamente, se você não quiser usar bodyParser, o seguinte 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');
});

2

Para Express 3.0, se você deseja usar os eventos formidáveis, deve remover o middleware multipartes, para que possa criar a nova instância dele.

Para fazer isso:

app.use(express.bodyParser());

Pode ser escrito como:

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

E agora crie o objeto de formulário:

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

Eu também postei isso no meu blog, Obtendo um objeto de formulário formidável no Express 3.0 no upload .


Sua sugestão é enganosa, o bodyParser basicamente analisa o formulário. e aceita variáveis ​​de configuração formidáveis.
Marius

1
@timoxley este é apenas um exemplo
Risto Novik

1

Eu sei que a pergunta original era relacionada a versões específicas, mas também se referia à "última" - a postagem de @JohnAllen não é mais relevante devido a Expressjs bodyParser e connect-form

Isso demonstra o bodyParser () embutido, 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');

0

Este é o meu método para upload de vários arquivos:

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

Certifique-se de que seu formulário tenha enctype = "multipart / form-data"

Espero que isso lhe dê uma mão;)


0

Esta é uma maneira de fazer upload de suas imagens usando o pacote formidável, que é recomendado em vez do bodyParser nas versões posteriores do Express. Isso também inclui a capacidade de redimensionar suas imagens na hora:

Do meu site: Carregando e redimensionando imagens (em tempo real) com Node.js e Express .

Aqui está a essência:

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: Isso requer Image Magick para o redimensionamento rápido do polegar.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.