Téléchargement d'images à l'aide de Node.js, Express et Mongoose

102

Veuillez envisager des réponses plus récentes qui contiennent des informations plus à jour, car les choses ont changé au fil des ans!

Étant donné que de nombreuses nouvelles bibliothèques Node.js sont rapidement rendues obsolètes et qu'il y a relativement peu d'exemples de toute façon, je veux poser des questions sur le téléchargement d'images en utilisant:

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

Comment les autres l'ont-ils fait?

J'ai trouvé: node-formidable , mais je suis nouveau dans le téléchargement d'images en général, donc je veux apprendre des choses générales et des façons de le faire en utilisant Node.js et Express.

JohnAllen
la source
12
Mise à jour Les nouvelles versions d'Express ont cette fonctionnalité intégrée, considérez qu'avant de passer du temps avec 'connect-form'
user531694
4
2015 tl; dr- envoyez des requêtes multipart / form à votre serveur et analysez-les avec Multer puisque BodyParser n'analyse plus les fichiers. npm install multer --savepuis dans votre application, vous pouvez accéder req.files.your_file_param_nameet enregistrer en s3 avec aws-sdkoufs.writeFile(...)
utilisateur

Réponses:

74

Je répondrai à ma propre question pour la première fois. J'ai trouvé un exemple directement à la source. Veuillez pardonner la mauvaise indentation. Je ne savais pas trop comment mettre en retrait correctement lors du copier-coller. Le code vient directement de l' multipart/form-dataexemple Express sur 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
la source
3
Ouais mais comment enregistrez-vous le fichier?
Nick Retallack
1
@NickRetallack le fichier enregistré est stocké dans files.image.path
Robin Duckett
@ robin-duckett, comment avez-vous spécifié le nom de fichier et le chemin au préalable?
Luc
4
@Luc: Vous ne le faites pas, il est enregistré dans un répertoire temporaire à partir duquel vous le déplacez ailleurs.
kevmo314
1
Voici comment configurer le répertoire de téléchargement dans express: // REMARQUE: utilisez un chemin absolu vers le répertoire de téléchargement pour éviter les problèmes dans les sous-modules! // app.use (express.bodyParser ({uploadDir: uploadDir}));
Risadinha le
47

Puisque vous utilisez express, ajoutez simplement bodyParser:

app.use(express.bodyParser());

alors votre itinéraire a automatiquement accès aux fichiers téléchargés dans 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 vous nommez le contrôle d'entrée "todo" comme ceci (en Jade):

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

Ensuite, le fichier téléchargé est prêt au moment où vous obtenez le chemin et le nom de fichier d'origine dans 'files.todo':

  • req.files.todo.path et
  • req.files.todo.name

autres propriétés utiles de req.files:

  • taille (en octets)
  • type (par exemple, 'image / png')
  • lastModifiedate
  • _writeStream.encoding (par exemple, 'binaire')
Brent Faust
la source
Je n'ai jamais entendu parler de cette façon et je vais aller de l'avant et dire que ces gars-là connaissent le meilleur developer.mozilla.org/en-US/docs/JavaScript/Guide/ ... donc je suppose que nous nous trompons tous les deux;)
srquinn
bodyParsern'est pas sûr, du moins d'après ceci: andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html . La réponse de Jon J a fonctionné pour moi.
Matt Browne
Par "non sécurisé", cela signifie simplement que des fichiers temporaires sont créés, de sorte qu'une "attaque" pourrait éventuellement remplir l'espace disque du serveur avec des fichiers temporaires. Il s'agit davantage d'un problème de robustesse, car ce n'est pas une faille de sécurité.
Brent Faust
19

Vous pouvez configurer le middleware de l'analyseur de corps de connexion dans un bloc de configuration de votre fichier d'application principal:

    /** Form Handling */
    app.use(express.bodyParser({
        uploadDir: '/tmp/uploads',
        keepExtensions: true
    }))
    app.use(express.limit('5mb'));
srquinn
la source
C'est en fait la meilleure façon de gérer les téléchargements, je suppose. Conservez le fichier si vous souhaitez le supprimer simplement plutôt que de le copier dans un emplacement séparé. Merci.
Eastern Monk
2
@AksharPrabhuDesai Oui et non. Disons que vous avez un outil de téléchargement / recadrage de photos. Si vous autorisez l'utilisateur à télécharger directement dans votre dossier public, vous avez une grave faille de sécurité. Dans ce cas, il est préférable de télécharger dans un dossier tmp, puis de passer dans votre dossier public après avoir confirmé que le fichier n'est pas un cheval de Troie.
srquinn
Ne semble plus être pris en charge. Cela ressemblait à une bonne solution.
Blaze
14

Vous voyez, la meilleure chose à faire est de simplement télécharger l'image sur le disque et d'enregistrer l'URL dans MongoDB. Reposez-vous lorsque vous récupérez à nouveau l'image. Spécifiez simplement l'URL et vous obtiendrez une image. Le code de téléchargement est le suivant.

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

Enregistrez maintenant le chemin cible dans votre base de données MongoDB.

Encore une fois, lors de la récupération de l'image, extrayez simplement l'URL de la base de données MongoDB et utilisez-la sur cette méthode.

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
la source
9

Essayez ce code, cela vous aidera.

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
la source
util.pump(ins, ous)est amorti, cela pourrait être fait ins.pipe(ous);maintenant. Mais cela supprimera-t-il le fichier image de l'ancien emplacement?
Emiel Vandenbussche
8

Vous pouvez également utiliser ce qui suit pour définir un chemin où il enregistre le fichier.

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

Encore une fois, si vous ne souhaitez pas utiliser bodyParser, ce qui suit fonctionne:

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
la source
2

Pour Express 3.0, si vous souhaitez utiliser les événements formidables, vous devez supprimer le middleware en plusieurs parties afin de pouvoir en créer la nouvelle instance.

Pour faire ça:

app.use(express.bodyParser());

Peut s'écrire:

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

Et maintenant, créez l'objet de formulaire:

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

J'ai également publié ceci sur mon blog, Obtenir un formidable objet de formulaire dans Express 3.0 lors du téléchargement .

Risto Novik
la source
Votre suggestion est trompeuse, le bodyParser analyse essentiellement le formulaire. et accepte de formidables variables de configuration.
Marius le
1
@timoxley ce n'est qu'un exemple
Risto Novik
1

Je sais que la question originale concernait des versions spécifiques, mais elle faisait également référence à la "dernière" - La publication de @JohnAllen n'est plus pertinente en raison d' Expressjs bodyParser et connect-form

Cela démontre le bodyParser () intégré facile à utiliser:

 /**
 * 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
la source
0

Voici ma méthode pour télécharger plusieurs fichiers:

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

Assurez-vous que votre formulaire contient enctype = "multipart / form-data"

J'espère que cela vous donne un coup de main;)

Despertaweb
la source
0

Voici un moyen de télécharger vos images à l'aide du formidable package, qui est recommandé par rapport à bodyParser dans les versions ultérieures d'Express. Cela inclut également la possibilité de redimensionner vos images à la volée:

Depuis mon site Web: Télécharger et redimensionner des images (à la volée) avec Node.js et Express .

Voici l'essentiel:

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

REMARQUE: Cela nécessite Image Magick pour le redimensionnement rapide du pouce.

Tony Spiro
la source