【问题标题】:Uploading images using Node.js, Express, and Mongoose使用 Node.js、Express 和 Mongoose 上传图片
【发布时间】:2011-07-06 04:38:07
【问题描述】:

请考虑更新的答案,这些答案包含更多最新信息,因为多年来情况发生了变化!

由于许多新的 Node.js 库很快就被淘汰了,而且无论如何我想问的关于上传图片的例子相对较少:

  • Node.js (v0.4.1)
  • Express (1.0.7)
  • 猫鼬 (1.1.0)。

其他人是怎么做到的?

我找到了:node-formidable,但我一般是上传图片的新手,所以我想学习使用 Node.js 和 Express 上传图片的一般知识和方法。

【问题讨论】:

  • 更新较新版本的 Express 内置了此功能,在花时间使用 'connect-form' 之前请考虑这一点
  • 2015 tl;dr- 将 multipart/form 请求发送到您的服务器并使用 Multer 解析它们,因为 BodyParser 不再解析文件。 npm install multer --save 然后在您的应用程序中您可以访问req.files.your_file_param_name 并使用aws-sdkfs.writeFile(...) 保存到s3

标签: node.js image file-upload express mongoose


【解决方案1】:

转换成字符串后存储文件将变得很容易,您只需在前端将字符串转换成图像即可

在您的 api 中使用此代码将图像转换为 base64 字符串,并且不要忘记从上传文件夹中删除文件

"img": new Buffer.from(fs.readFileSync(req.file.path)).toString("base64")

删除文件

       let resultHandler = function (err) {
            if (err) {
                console.log("unlink failed", err);
            } else {
                console.log("file deleted");
            }
        }

        fs.unlink(req.file.path, resultHandler);

在你的路线上导入 multer

 `multer const multer = require('multer');
  const upload = multer({ dest: __dirname + '/uploads/images' });`
  Add upload.single('img') in your request

  router.post('/fellows-details', authorize([Role.ADMIN, Role.USER]), 
        upload.single('img'), usersController.fellowsdetails);

如果您想将图像保存在localstorage 并希望将路径保存在数据库中,您可以尝试以下方法

您必须先安装将创建文件夹的 fs-extra。我正在按 id 创建单独的文件夹,如果你想删除它,你可以删除它。和 要保存上传图像的路径,请将此代码添加到您用于保存图像的 api 或控制器中,并将其与其他数据一起添加到数据库中

    let Id = req.body.id;
    let path = `tmp/daily_gasoline_report/${Id}`;

multer 创建单独的文件夹,例如multerHelper.js

 const multer = require('multer');
 let fs = require('fs-extra');

 let storage = multer.diskStorage({
 destination: function (req, file, cb) {
    let Id = req.body.id;
    let path = `tmp/daily_gasoline_report/${Id}`;
    fs.mkdirsSync(path);
    cb(null, path);
 },
 filename: function (req, file, cb) {
    // console.log(file);

  let extArray = file.mimetype.split("/");
  let extension = extArray[extArray.length - 1];
  cb(null, file.fieldname + '-' + Date.now() + "." + extension);
 }
})

let upload = multer({ storage: storage });

let createUserImage = upload.array('images', 100);

let multerHelper = {
     createUserImage,
}

   module.exports = multerHelper;

在您的路线中导入multerhelper 文件

   const multerHelper = require("../helpers/multer_helper");

   router.post(multerHelper. createUserImage , function(req, res, next) {
      //Here accessing the body datas.
    })

【讨论】:

    【解决方案2】:

    这是一种使用强大的包上传图片的方法,在 Express 的更高版本中推荐使用该包而不是 bodyParser。这还包括动态调整图像大小的能力:

    来自我的网站:Uploading and Resizing Images (on the fly) With Node.js and Express

    这里是要点:

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

    注意:这需要 Image Magick 来快速调整拇指大小。

    【讨论】:

      【解决方案3】:

      有我的方法多上传文件:

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

      确保您的表单具有 enctype="multipart/form-data"

      我希望这能帮到你;)

      【讨论】:

        【解决方案4】:

        created an example 使用 Express 和 Multer。它非常简单,避免了所有 Connect 警告

        它可能对某人有所帮助。

        【讨论】:

        【解决方案5】:

        看,您能做的最好的事情就是将图像上传到磁盘并将 URL 保存在 MongoDB 中。再次检索图像时休息。只需指定 URL,您将获得一张图片。上传代码如下。

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

        现在将目标路径保存在您的 MongoDB 数据库中。

        同样,在检索图像时,只需从 MongoDB 数据库中提取 URL,并将其用于此方法。

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

        【讨论】:

          【解决方案6】:

          对于 Express 3.0,如果要使用强大的事件,则必须删除多部分中间件,以便创建它的新实例。

          为此:

          app.use(express.bodyParser());
          

          可以写成:

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

          现在创建表单对象:

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

          我也在我的博客上发布了这个,Getting formidable form object in Express 3.0 on upload

          【讨论】:

          • 您的建议具有误导性,bodyParser 基本上会解析表单。并接受强大的配置变量。
          • @timoxley 这只是示例
          【解决方案7】:

          我知道原始问题与特定版本有关,但它也提到了“最新” - @JohnAllen 的帖子由于Expressjs bodyParser and connect-form而不再相关

          这演示了易于使用的内置 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');
          

          【讨论】:

            【解决方案8】:

            您可以在主应用程序文件的配置块中配置连接主体解析器中间件:

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

            【讨论】:

            • 这实际上是我认为处理上传的最佳方式。如果您想删除文件而不是将其复制到单独的位置,请保留该文件。谢谢。
            • @AksharPrabhuDesai 是也不是。假设您有一个照片上传/裁剪工具。如果您允许用户直接上传到您的公用文件夹,则存在严重的安全漏洞。这种情况下,最好上传到tmp文件夹,确认不是木马后移入公用文件夹。
            • 似乎不再受支持。看起来是个不错的解决方案。
            【解决方案9】:

            您还可以使用以下设置保存文件的路径。

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

            【讨论】:

              【解决方案10】:

              同样,如果您不想使用 bodyParser,可以使用以下方法:

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

              【讨论】:

                【解决方案11】:

                由于您使用的是 express,因此只需添加 bodyParser:

                app.use(express.bodyParser());
                

                那么你的路由会自动访问 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
                });
                

                如果您像这样(在 Jade 中)将输入控件命名为“todo”:

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

                那么当你在'files.todo'中获得路径和原始文件名时,上传的文件就准备好了:

                • req.files.todo.path 和
                • req.files.todo.name

                其他有用的 req.files 属性:

                • 大小(以字节为单位)
                • 类型(例如,'image/png')
                • 最后修改日期
                • _writeStream.encoding(例如,'binary')

                【讨论】:

                【解决方案12】:

                我将第一次回答我自己的问题。我直接从源代码中找到了一个示例。请原谅可怜的缩进。我不确定在复制和粘贴时如何正确缩进。代码直接来自 GitHub 上的 Express multipart/form-data example

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

                【讨论】:

                • 是的,但是如何保存文件?
                • @NickRetallack 保存的文件存储在 files.image.path
                • @robin-duckett,你是怎么事先指定文件名和路径的?
                • @Luc:你没有,它被保存到一个临时目录中,你可以从中将它移到其他地方。
                • 这是在 express 中配置上传目录的方式: // 注意:使用上传目录的绝对路径以避免子模块中的问题! // app.use(express.bodyParser({ uploadDir: uploadDir }));
                【解决方案13】:

                试试这个代码。它会有所帮助。

                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) 已折旧,现在可以使用ins.pipe(ous); 完成。但这会删除旧位置上的图像文件吗?
                猜你喜欢
                • 2017-02-10
                • 2018-12-17
                • 2021-07-05
                • 2016-06-14
                • 2018-01-14
                • 2018-03-19
                • 2017-06-19
                • 2018-11-23
                • 2020-11-06
                相关资源
                最近更新 更多