首页 文章

使用NodeJS将文件上载到Amazon S3

提问于
浏览
45

我在尝试将文件上传到S3存储桶时遇到了问题 . 除了我的文件参数看起来不合适之外,一切正常 . 我正在使用Amazon S3 sdk从nodejs上传到s3 .

这些是我的路线设置:

var multiparty = require('connect-multiparty'),
    multipartyMiddleware = multiparty();
app.route('/api/items/upload').post(multipartyMiddleware, items.upload);

这是items.upload()函数:

exports.upload = function(req, res) {
    var file = req.files.file;
    var s3bucket = new AWS.S3({params: {Bucket: 'mybucketname'}});
    s3bucket.createBucket(function() {
        var params = {
            Key: file.name,
            Body: file
        };
        s3bucket.upload(params, function(err, data) {
            console.log("PRINT FILE:", file);
            if (err) {
                console.log('ERROR MSG: ', err);
            } else {
                console.log('Successfully uploaded data');
            }
        });
    });
};

Body param设置为类似 "hello" 的字符串可以正常工作 . 根据docBody param必须采用(Buffer,Typed Array,Blob,String,ReadableStream)对象数据 . 但是,上载文件对象失败,并显示以下错误消息:

[Error: Unsupported body payload object]

这是文件对象:

{ fieldName: 'file',
  originalFilename: 'second_fnp.png',
  path: '/var/folders/ps/l8lvygws0w93trqz7yj1t5sr0000gn/T/26374-7ttwvc.png',
  headers: 
   { 'content-disposition': 'form-data; name="file"; filename="second_fnp.png"',
     'content-type': 'image/png' },
  ws: 
   { _writableState: 
      { highWaterMark: 16384,
        objectMode: false,
        needDrain: true,
        ending: true,
        ended: true,
        finished: true,
        decodeStrings: true,
        defaultEncoding: 'utf8',
        length: 0,
        writing: false,
        sync: false,
        bufferProcessing: false,
        onwrite: [Function],
        writecb: null,
        writelen: 0,
        buffer: [],
        errorEmitted: false },
     writable: true,
     domain: null,
     _events: { error: [Object], close: [Object] },
     _maxListeners: 10,
     path: '/var/folders/ps/l8lvygws0w93trqz7yj1t5sr0000gn/T/26374-7ttwvc.png',
     fd: null,
     flags: 'w',
     mode: 438,
     start: undefined,
     pos: undefined,
     bytesWritten: 261937,
     closed: true },
  size: 261937,
  name: 'second_fnp.png',
  type: 'image/png' }

任何帮助将不胜感激!

4 回答

  • 80

    所以看起来这里有一些问题 . 根据您的帖子,您似乎尝试使用 connect-multiparty 中间件支持文件上传 . 这个中间件的作用是获取上传的文件,将其写入本地文件系统,然后将 req.files 设置为上传的文件 .

    您的路线配置看起来很好,问题似乎与您的 items.upload() 功能有关 . 特别是这部分:

    var params = {
      Key: file.name,
      Body: file
    };
    

    正如我在回答的开头提到的 connect-multiparty 将文件写入本地文件系统,因此您需要打开文件并读取它,然后上传它,然后在本地文件系统上删除它 .

    那说你可以将你的方法更新为如下所示:

    var fs = require('fs');
    exports.upload = function (req, res) {
        var file = req.files.file;
        fs.readFile(file.path, function (err, data) {
            if (err) throw err; // Something went wrong!
            var s3bucket = new AWS.S3({params: {Bucket: 'mybucketname'}});
            s3bucket.createBucket(function () {
                var params = {
                    Key: file.originalFilename, //file.name doesn't exist as a property
                    Body: data
                };
                s3bucket.upload(params, function (err, data) {
                    // Whether there is an error or not, delete the temp file
                    fs.unlink(file.path, function (err) {
                        if (err) {
                            console.error(err);
                        }
                        console.log('Temp File Delete');
                    });
    
                    console.log("PRINT FILE:", file);
                    if (err) {
                        console.log('ERROR MSG: ', err);
                        res.status(500).send(err);
                    } else {
                        console.log('Successfully uploaded data');
                        res.status(200).end();
                    }
                });
            });
        });
    };
    

    这样做是从本地文件系统读取上传的文件,然后将其上传到S3,然后删除临时文件并发送响应 .

    这种方法存在一些问题 . 首先,它没有那么高效,因为对于大文件,您将在编写之前加载整个文件 . 其次,这个过程不支持大文件的多部分上传(我认为在你必须进行多部分上传之前,截止值是5 Mb) .

    我建议的是你使用我一直在研究的模块S3FS,它提供了与Node.JS中原生FS类似的接口,但抽象了一些细节,如多部分上传和S3 api (以及添加一些额外的功能,如递归方法) .

    如果你要拉入S3FS库,你的代码看起来像这样:

    var fs = require('fs'),
        S3FS = require('s3fs'),
        s3fsImpl = new S3FS('mybucketname', {
            accessKeyId: XXXXXXXXXXX,
            secretAccessKey: XXXXXXXXXXXXXXXXX
        });
    
    // Create our bucket if it doesn't exist
    s3fsImpl.create();
    
    exports.upload = function (req, res) {
        var file = req.files.file;
        var stream = fs.createReadStream(file.path);
        return s3fsImpl.writeFile(file.originalFilename, stream).then(function () {
            fs.unlink(file.path, function (err) {
                if (err) {
                    console.error(err);
                }
            });
            res.status(200).end();
        });
    };
    

    这将做的是实例化所提供的存储桶和AWS凭据的模块,然后创建存储桶(如果它不存在) . 然后,当请求通过上传文件时,我们将打开一个流到该文件并使用它将文件写入S3到指定的路径 . 这将在幕后处理多部分上传部分(如果需要),并且具有通过流完成的好处,因此您无需等待在开始上载之前读取整个文件 .

    如果您愿意,可以将代码更改为Promises的回调 . 或者将pipe()方法与事件侦听器一起使用以确定结束/错误 .

    如果您正在寻找其他方法,请查看s3fs的文档,如果您正在寻找其他方法或遇到问题,请随时打开问题 .

  • 3

    我发现以下是一个有效的解决方案::

    npm install aws-sdk
    

    安装aws-sdk后,使用以下代码将值替换为您需要的值 .

    var AWS = require('aws-sdk');
    var fs =  require('fs');
    
    var s3 = new AWS.S3();
    
    // Bucket names must be unique across all S3 users
    
    var myBucket = 'njera';
    
    var myKey = 'jpeg';
    //for text file
    //fs.readFile('demo.txt', function (err, data) {
    //for Video file
    //fs.readFile('demo.avi', function (err, data) {
    //for image file                
    fs.readFile('demo.jpg', function (err, data) {
      if (err) { throw err; }
    
    
    
         params = {Bucket: myBucket, Key: myKey, Body: data };
    
         s3.putObject(params, function(err, data) {
    
             if (err) {
    
                 console.log(err)
    
             } else {
    
                 console.log("Successfully uploaded data to myBucket/myKey");
    
             }
    
          });
    
    });
    

    如果您正在寻找参考资料,我在这里找到了关于这个主题的完整教程::


    How to upload files (text/image/video) in amazon s3 using node.js

  • 10

    或使用承诺:

    const AWS = require('aws-sdk');
      AWS.config.update({
        accessKeyId: 'accessKeyId',
        secretAccessKey: 'secretAccessKey',
        region: 'region'
      });
    
    let params = {
            Bucket: "yourBucketName",
            Key: 'someUniqueKey',
            Body: 'someFile'
          };
          try {
            let uploadPromise = await new AWS.S3().putObject(params).promise();
            console.log("Successfully uploaded data to bucket");
          } catch (e) {
            console.log("Error uploading data: ", e);
          }
    
  • 0
    var express = require('express')
    
    app = module.exports = express();
    var secureServer = require('http').createServer(app);
    secureServer.listen(3001);
    
    var aws = require('aws-sdk')
    var multer = require('multer')
    var multerS3 = require('multer-s3')
    
        aws.config.update({
        secretAccessKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        accessKeyId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        region: 'us-east-1'
        });
        s3 = new aws.S3();
    
       var upload = multer({
       storage: multerS3({
        s3: s3,
        dirname: "uploads",
        bucket: "Your bucket name",
        key: function (req, file, cb) {
            console.log(file);
            cb(null, "uploads/profile_images/u_" + Date.now() + ".jpg"); //use  
         Date.now() for unique file keys
        }
      })
       });
    
     app.post('/upload', upload.single('photos'), function(req, res, next) {
    
     console.log('Successfully uploaded ', req.file)
    
     res.send('Successfully uploaded ' + req.file.length + ' files!')
    
    })
    

相关问题