1

I have spent days trying to figure out how to post photos to my AWS bucket and have read a lot of similar questions on SO but nothing seems to be working and I almost always get an "unexpected field" error.

I have confirmed the libraries are the correct versions; added bodyParser; added region / signatureVersion; and have tried changing 'upload.single('image') a variety of ways.

In my Model I am trying to save the image url to a field name 'image-field' but using it doesn't change the error I am getting. I am only trying to have this work on my backend before attempting the FE and am using with PostMan.

Please help, I am losing my mind over here.

import express from 'express';
import aws from 'aws-sdk';
import multer from 'multer';
import multerS3 from 'multer-s3';
import bodyParser from 'body-parser';

const router = express.Router();
router.use(bodyParser.json())

aws.config.update({
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
  region: 'us-west-2',
  signatureVersion: 'v4'
})

const s3 = new aws.S3()

const upload = multer({
  storage: multerS3({
    s3: s3,
    bucket: 'bucket-name',
    acl: "public-read",
    metadata: function (req, file, cb) {
      cb(null, { fieldName: file.fieldname });
    },
    key: function (req, file, cb) {
      cb(null, Date.now().toString() + '-' + file.originalname);
    },
  }),
})

router.post('/', upload.single('image'), (req, res, next) => {
  console.log('Uploaded!')
  res.send(req.file)
})

export default router;

PostMan enter image description here

Note: If it helps, I am including old code I was using successfully for my FE and BE to save images locally and then the urls on Mongo.

import path from 'path';
import express from 'express';
import multer from 'multer';

const router = express.Router();

const storage = multer.diskStorage({
destination(req, file, cb) {
  cb(null, 'uploads/')
},
filename(req, file, cb) {
  cb(null, `${file.fieldname}-${Date.now()}${path.extname(file.originalname)}`)
}
})

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

if(extname && mimetype) {
  return cb(null, true)
} else {
  cb('Images only!')
}
}

const upload = multer({
storage,
fileFilter: function(req, file, cb) {
  checkFileType(file, cb)
}
})

router.post('/', upload.single('image'), (req, res) => {
res.send(`/${req.file.path}`)
})

export default router;
jasontulloch
  • 31
  • 1
  • 6

1 Answers1

0

We did it! See below code below, but the update really pertains to changing the POST API from .single() to .any() and then setting fileFilter to remove any files you do not want to include. I will note that PostMan gives me an error on my route when I do post the file, but the image does successfully upload to the s3 bucket.

Said another way, .any() "Accepts all files that comes over the wire. An array of files will be stored in req.files". Since we know we are taking any file, we must use fileFilter to define which files we want to be able to post. Essentially, if you don't use fileFilter, you can post any file you want.

import express from 'express';
import aws from 'aws-sdk';
import multer from 'multer';
import multerS3 from 'multer-s3';
import path from 'path';

const router = express.Router();

aws.config.update({
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
  region: 'us-west-2',
  signatureVersion: 'v4'
})

const s3 = new aws.S3()

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

  if(extname && mimetype) {
    return cb(null, true)
  } else {
    cb('Images only!')
  }
}

const upload = multer({
  limits: { fileSize: 2000000 },
  fileFilter: function(req, file, cb) {
    checkFileType(file, cb)
  },
  storage: multerS3({
    s3: s3,
    bucket: 'recipebook-recipe-cover-images',
    acl: "public-read",
    contentType: multerS3.AUTO_CONTENT_TYPE,
    metadata: function (req, file, cb) {
      cb(null, { fieldName: file.fieldname });
    },
    key: function (req, file, cb) {
      cb(null, Date.now().toString() + '-' + file.originalname);
    },
  }),
})

router.post('/', upload.any())

export default router;
jasontulloch
  • 31
  • 1
  • 6