使用multer-s3 nodejs将图像上传到亚马逊S3

42

我正在尝试使用 multer-s3 将图片上传到亚马逊 S3,但是出现了以下错误:

TypeError: 预期opts.s3是一个对象 node_modules/multer-s3/index.js:69:20

这是我的服务器代码:

var upload = multer({
    storage: s3({
        dirname: '/',
        bucket: 'bucket',
        secretAccessKey: 'key',
        accessKeyId: 'key',
        region: 'us-west-2',
        filename: function (req, file, cb) {
            cb(null, file.originalname); 
        }
    })
});

app.post('/upload', upload.array('file'), function (req, res, next) {
    res.send("Uploaded!");
});

为什么我会收到这个错误?

8个回答

108

[2022年3月更新]它一直正常工作,现在还显示了已上传文件的公共URL。

完整和可用的Node Cheat | 使用Multer-S3上传到S3

代码:

var express = require('express'),
    aws = require('aws-sdk'),
    bodyParser = require('body-parser'),
    multer = require('multer'),
    multerS3 = require('multer-s3');

aws.config.update({
    secretAccessKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
    accessKeyId: 'XXXXXXXXXXXXXXX',
    region: 'us-east-1'
});

var app = express(),
    s3 = new aws.S3();

app.use(bodyParser.json());

var upload = multer({
    storage: multerS3({
        s3: s3,
        acl: 'public-read',
        bucket: 'bucket-name',
        key: function (req, file, cb) {
            console.log(file);
            cb(null, file.originalname); //use Date.now() for unique file keys
        }
    })
});

//open in browser to see upload form
app.get('/', function (req, res) {
    res.sendFile(__dirname + '/index.html');//index.html is inside node-cheat
});

//use by upload form
app.post('/upload', upload.array('upl', 25), function (req, res, next) {
    res.send({
        message: "Uploaded!",
        urls: req.files.map(function(file) {
            return {url: file.location, name: file.key, type: file.mimetype, size: file.size};
        })
    });
});
  
app.listen(3000, function () {
    console.log('Example app listening on port 3000!');
});

获取完整代码库:

克隆 node-cheat 的 express_multer_s3,运行 node app,然后执行 npm install express body-parser aws-sdk multer multer-s3

祝您帮助愉快!


5
为什么这个答案没有一百万个赞?这对我很有帮助。谢谢。你可能需要更新:aws.config.update({ signatureVersion: 'v4',因为我遇到了一个错误,这样做解决了它。 - Somename
1
非常有帮助。 - Nilesh Modak
4
我们如何获取上传文件的S3 URL? - Pulkit Aggarwal
它应该在res.files.location @PulkitAggarwal中。 - Tyler Chong
至少告诉我问题,这样我才能了解你实际面临的情况 :) 或者访问链接:https://github.com/zishon89us/node-cheat/tree/master/aws/express_multer_s3 以了解正确的软件包版本。 - Zeeshan Hassan Memon
显示剩余7条评论

21

@V31已经回答得很好了,但我还想再补充一点。

我认为将一个职责放入一个文件中可以更好地组织代码并进行调试。

我创建了一个用于上传的文件upload.js

require('dotenv').config();
const AWS = require('aws-sdk');
const multer = require('multer');
const multerS3 = require('multer-s3');

const s3Config = new AWS.S3({
    accessKeyId: process.env.AWS_IAM_USER_KEY,
    secretAccessKey: process.env.AWS_IAM_USER_SECRET,
    Bucket: process.env.AWS_BUCKET_NAME
  });

const fileFilter = (req, file, cb) => {
    if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
        cb(null, true)
    } else {
        cb(null, false)
    }
}

// this is just to test locally if multer is working fine.
const storage = multer.diskStorage({
    destination: (req, res, cb) => {
        cb(null, 'src/api/media/profiles')
    },
    filename: (req, file, cb) => {
        cb(null, new Date().toISOString() + '-' + file.originalname)
    }
})

const multerS3Config = multerS3({
    s3: s3Config,
    bucket: process.env.AWS_BUCKET_NAME,
    metadata: function (req, file, cb) {
        cb(null, { fieldName: file.fieldname });
    },
    key: function (req, file, cb) {
        console.log(file)
        cb(null, new Date().toISOString() + '-' + file.originalname)
    }
});

const upload = multer({
    storage: multerS3Config,
    fileFilter: fileFilter,
    limits: {
        fileSize: 1024 * 1024 * 5 // we are allowing only 5 MB files
    }
})

exports.profileImage = upload; 

这是在我的路由routes.js中导入的

const express = require('express');

const ProfileController = require('../profile/controller');
const { profileImage } = require('../utils/upload.js'); 

 const routes = (app) => {
    const apiRoutes = express.Router();

    apiRoutes.use('/profile', profileRoutes);
    profileRoutes.post('/',profileImage.single('profileImage'), ProfileController.saveProfile);

    app.use('/api', apiRoutes);

 }

module.exports = routes

使用Postman发送POST请求时,需要在请求中添加请求体。下图是一个示例,展示了如何在Postman中添加请求体。

在这里输入图片描述


请问您能否发布从Angular前端或Postman客户端调用的请求和主体的详细信息? - Satyam
@Satyam,请查看Postman中的帖子正文附件截图。 - Anjum....
1
@Anjum已经回答得非常好了,但我还想再补充一点。 - Zeeshan Hassan Memon

10

我只是想要添一些我的意见,

在所有的回答中有很多评论,比如如何在上传后获取公共URLS3响应对象,我们来看看实现和案例。

// INITIALIZE NPMS
var AWS = require('aws-sdk'),
multer = require('multer'),
multerS3 = require('multer-s3'),
path = require('path');

// CONFIGURATION OF S3
AWS.config.update({
    secretAccessKey: '***********************************',
    accessKeyId: '****************',
    region: 'us-east-1'
});

// CREATE OBJECT FOR S3
const S3 = new AWS.S3();
// CREATE MULTER FUNCTION FOR UPLOAD
var upload = multer({
    // CREATE MULTER-S3 FUNCTION FOR STORAGE
    storage: multerS3({
        s3: S3,
        acl: 'public-read',
        // bucket - WE CAN PASS SUB FOLDER NAME ALSO LIKE 'bucket-name/sub-folder1'
        bucket: 'bucket-name',
        // META DATA FOR PUTTING FIELD NAME
        metadata: function (req, file, cb) {
            cb(null, { fieldName: file.fieldname });
        },
        // SET / MODIFY ORIGINAL FILE NAME
        key: function (req, file, cb) {
            cb(null, file.originalname); //set unique file name if you wise using Date.toISOString()
            // EXAMPLE 1
            // cb(null, Date.now() + '-' + file.originalname);
            // EXAMPLE 2
            // cb(null, new Date().toISOString() + '-' + file.originalname);

        }
    }),
    // SET DEFAULT FILE SIZE UPLOAD LIMIT
    limits: { fileSize: 1024 * 1024 * 50 }, // 50MB
    // FILTER OPTIONS LIKE VALIDATING FILE EXTENSION
    fileFilter: function(req, file, cb) {
        const filetypes = /jpeg|jpg|png/;
        const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
        const mimetype = filetypes.test(file.mimetype);
        if (mimetype && extname) {
            return cb(null, true);
        } else {
            cb("Error: Allow images only of extensions jpeg|jpg|png !");
        }
    }
});

如果我们想要在上传后从S3检索res对象,有三种情况:

情况1:当我们使用.single(fieldname)方法时,它将在req.file中返回文件对象。

app.post('/upload', upload.single('file'), function (req, res, next) {
    console.log('Uploaded!');
    res.send(req.file);
});

情况2: 当我们使用.array(fieldname[, maxCount])方法时,它将返回文件对象在req.files中。

app.post('/upload', upload.array('file', 1), function (req, res, next) {
    console.log('Uploaded!');
    res.send(req.files);
});

案例 3: 当我们使用.fields(fields)方法时,它会在req.files中返回文件对象。

app.post('/upload', upload.fields([
  { name: 'avatar', maxCount: 1 },
  { name: 'gallery', maxCount: 8 }
]), function (req, res, next) {
    console.log('Uploaded!');
    res.send(req.files);
});

文件的URL将会在req.file.location中。 - Pedro Contipelli

6

s3需要作为一个对象进行传递。根据文档,这个对象需要像这样:

var upload = multer({
  storage: multerS3({
    s3: s3,
    bucket: 'some-bucket',
    metadata: function (req, file, cb) {
      cb(null, {fieldName: file.fieldname});
    },
    key: function (req, file, cb) {
      cb(null, Date.now().toString())
    }
  })
})

MulterS3 Docs


以上代码中应该在哪里提供accesskeyId? - mBlaze
@mBlaze:你需要使用aws sdk创建一个s3对象,可以使用var s3 = new aws.S3({ /* ... */ })。你可以通过以下链接找到更多信息http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-json-file.html。 - V31
@mBlaze:只是想知道您是否已经解决了问题。如果是的话,请标记答案为已接受。 - V31

2
    //here is the function for upload the images on aws bucket using multer
    
    const path = require('path');
    const fs = require('fs');
    const aws = require('aws-sdk');
    const multer = require('multer');
    const multerS3 = require('multer-s3');
    
    
    aws.config.update({
        secretAccessKey: '**************************',
        accessKeyId: '********************',
        region: '**********************'
    });
    
    s3 = new aws.S3();
    
    
    const storage = multerS3({
        s3: s3,
        bucket: 'bucket-name',
        key: function(req, file, cb) {
            console.log(file);
            cb(null, file.originalname);
        }
    })
    
    //export the created function
    exports.uploadVideo = multer({ storage: storage }).single('file_name');
    
 //================================================================================   
    
    //import uploadVideo function whenever you need to upload the file on aws s3 bucket
    const { uploadVideo } = require('../../services/upload');
    
    exports.videoUpload = (req, res) => {
        uploadVideo(req, res, function(err) {
            if (err) {
                console.log(err);
                res.json({ status: 401, msg: err.message });
            } else {
                const image = getImagePath(req.file.filename);
                res.json({ status: 200, msg: 'Image uploaded sucess.', data: image });
            }
        });
    }
    //================================================================================
    
    //here is route file
    router.post('/video-upload',uploadController.videoUpload);

2

/*** 使用Multer上传图片 图片正在上传中 */

const fileStorage = multer.diskStorage({
  destination: function(req, file, cb) {
    cb(null, "./public/uploads");
  },
  filename: function(req, file, cb) {
    cb(null, file.originalname);
  }
});

/** AWS catalog */

aws.config.update({
  secretAccessKey: process.env.SECRET_KEY,
  accessKeyId: process.env.ACCESS_KEY,
  region: "us-east-1"
});

const s3 = new aws.S3();
const awsStorage = multerS3({
  s3: s3,
  bucket: process.env.BUCKET_NAME,
  key: function(req, file, cb) {
    console.log(file);
    cb(null, file.originalname);
  }
});

const upload = multer({
  storage: awsStorage(),
  /** in above line if you are using local storage in ./public/uploads folder than use
   ******* storage: fileStorage,
   * if you are using aws s3 bucket storage than use
   ******* storage: awsStorage(),
   */
  limits: { fileSize: 5000000 },
  fileFilter: function(req, file, cb) {
    checkFileType(file, cb);
  }
});
app.post("/user-profile-image", upload.single("profile"), (req, res, err) => {
  try {
    res.send(req.file);
  } catch (err) {
    res.send(400);
  }
});

const checkFileType = (file, cb) => {
  const filetypes = /jpeg|jpg|png|gif/;
  const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
  const mimetype = filetypes.test(file.mimetype);

  if (mimetype && extname) {
    return cb(null, true);
  } else {
    cb("Error: Images Only!");
  }
};

2
欢迎来到 SO :) 请编辑您的答案,解释您的代码如何解决 OP 面临的问题。 - Aimery

0

我们可以使用multer-s3将图像/ CSV / EXCEL文件上传到AWS s3。

我正在使用.single(fieldname)方法上传单个文件。

const aws = require('aws-sdk');
const multer = require('multer');
const multerS3 = require('multer-s3');

const s3 = new aws.S3({
  accessKeyId: process.env.AWS_ACCESS_KEY,
    secretAccessKey: process.env.AWS_SECRET_KEY,
    region: process.env.REGION,
});

 const upload = multer({
   storage: multerS3({
     s3: s3,
     bucket: process.env.AWS_S3_BUCKET,
     metadata: function (req, file, cb) {
       cb(null, {fieldName: 'Meta_Data'});
     },
     key: function (req, file, cb) {
      cb(null, file.originalname);
     },
     limits: {
        fileSize: 1024 * 1024 * 5 //  allowed only 5 MB files
    }
   })
 }).single('file');
 
exports.uploadfile = async(req,res,next)=>{
   try{ 
         upload(req,res, function(err){
             if(err){
                 console.log(err);
             }
             console.log(req.file.location);    
   })
})
}catch (err){
   res.status(400).json({
       status : 'fail', 
       message : err.message
   });
}
}

在路由文件中

router.route('/')
      .post(imageController.uploadfile);


0

我在以大写形式将S3传递给mutler,就像这样

S3: {object}

将其更改为小写的 s3 对我有效:

s3: {object}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接