Uploading image to amazon s3 using multer-s3 nodejs

65,433

Solution 1

[Update Mar 2022] It works perfectly fine till-date and now also shows the uploaded file public URL as well.

Complete and working Node Cheat | Upload to s3 using multer-s3 available.

Code:

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

For complete repo:

Clone node-cheat express_multer_s3, run node app followed by npm install express body-parser aws-sdk multer multer-s3.

Happy Helping!

Solution 2

@V31 has answered very well still I want to add my 2 cents.

I believe in keeping one responsibility into one file, for better code organization and debugging purpose.

I have created a file for uploading 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; 

Which is imported inside my routes 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 screen shot for post body

enter image description here

Solution 3

I just want to add my cents,

There are many comments in all answers like how to get public URL after uploading and S3 response object and lets see implementation and cases,

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

There are three cases, if we want to retrieve files res object from S3 after upload:

Case 1: When we are using .single(fieldname) method it will return file object in req.file

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

Case 2: When we are using .array(fieldname[, maxCount]) method it will return file object in req.files

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

Case 3: When we are using .fields(fields) method it will return file object in 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);
});

Solution 4

s3 needs to be an object to be passed. According to the docs, the object needs to be like this:

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

Solution 5

    //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);
Share:
65,433
mBlaze
Author by

mBlaze

Updated on February 22, 2021

Comments

  • mBlaze
    mBlaze about 3 years

    I am trying to upload an image to amazon s3 using multer-s3, but I am getting this error:

    TypeError: Expected opts.s3 to be object node_modules/multer-s3/index.js:69:20

    This is my server code:

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

    Why I am getting this error?

  • mBlaze
    mBlaze over 7 years
    Where to provide the accesskeyId for above
  • V31
    V31 over 7 years
    @mBlaze: you need to create a s3 object using var s3 = new aws.S3({ /* ... */ }) using the aws sdk. you can find more info using the link docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/…
  • V31
    V31 over 7 years
    @mBlaze: Just wondering if you were able to resolve your issue. If yes then please mark the answer accepted.
  • Somename
    Somename about 7 years
    Why aren't there a million likes for this answer? This helped me. Thanks. You may want to update : aws.config.update({ signatureVersion: 'v4', as I was getting an error and doing this solved it.
  • Nilesh Modak
    Nilesh Modak over 6 years
    Really helpful.
  • Mobasher Fasihy
    Mobasher Fasihy about 5 years
    for me the same line of codes shows this error "error":{"errno":-4058,"syscall":"open","code":"ENOENT","pat‌​h":"undefined/undefi‌​ned"}
  • Pulkit Aggarwal
    Pulkit Aggarwal almost 5 years
    How we can get the s3 url where we upload the file.
  • Satyam
    Satyam almost 5 years
    Can you please post the details of the request and body that has to be called from Angular front end or from postman client?
  • Anjum....
    Anjum.... almost 5 years
    @Satyam Please check attachment screenshot for post body in Postman
  • Anns Rahim
    Anns Rahim over 4 years
    I tried this code and i got a error like "Error: Unexpected end of multipart data" i dont know why i was searching for solution for couple of days please help me
  • Aimery
    Aimery over 4 years
    Welcome to SO :) Please edit your answer to explain how your code solves the problem OP is facing.
  • Zeeshan Hassan Memon
    Zeeshan Hassan Memon over 4 years
    next edit may be :) @Anjum.... has answered very well still I want to add my 2 cents.
  • Angelzzz
    Angelzzz about 4 years
    Thanks. It works to download file, but when I try to use thi link, it show access denied. How to handle this?
  • Zeeshan Hassan Memon
    Zeeshan Hassan Memon about 4 years
    @Angelzzz pass acl: 'public-read' above bucket: 'bucket-name' to get desired results.
  • kd12345
    kd12345 over 3 years
    Hi, can you please have a look at my question here, would really appreciate the help.stackoverflow.com/questions/65479245/nodejs-multer-aws-‌​s3
  • kd12345
    kd12345 over 3 years
    @ZeeshanHassanMemon Hi, hope you are good. Can you please have a look at my question would really appreciate the help. stackoverflow.com/questions/65479245/nodejs-multer-aws-s3
  • Tyler Chong
    Tyler Chong about 3 years
    It should be in res.files.location @PulkitAggarwal
  • Pedro Contipelli
    Pedro Contipelli over 2 years
    The file URL will be in req.file.location
  • murage kibicho
    murage kibicho about 2 years
    2022 update : This solution does not work anymore
  • kd12345
    kd12345 about 2 years
    hi can you please have a look at my question. stackoverflow.com/questions/71724311/nodejs-multer-s3-sequel‌​ize
  • kd12345
    kd12345 about 2 years
    hi can you please have alook at my question stackoverflow.com/questions/71724311/nodejs-multer-s3-sequel‌​ize
  • kd12345
    kd12345 about 2 years
    hi can you please have a look at my question stackoverflow.com/questions/71724311/nodejs-multer-s3-sequel‌​ize
  • Zeeshan Hassan Memon
    Zeeshan Hassan Memon about 2 years
    March 2022 update: It works perfectly fine, just tested it myself. @muragekibicho how can I help you?
  • murage kibicho
    murage kibicho about 2 years
    No offence, it does not work - that's why I said it does not work. It may work on your device because of the library versions you are using. @ZeeshanHassanMemon You can help me by letting people know this solution does not work in 2022
  • Zeeshan Hassan Memon
    Zeeshan Hassan Memon about 2 years
    At least tell me the issue so that I can understand what are you facing actually :) or visit the link: github.com/zishon89us/node-cheat/tree/master/aws/… to know the proper package versions.