0

I have been trying to use a MVC structure in Node.js to build a post route which both uploads to Mongodb as well as Amazon S3.

I have built below to help me upload to S3.

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

aws.config.update({
  secretAccessKey: process.env.AMAZON_SECRET_ACCESS_KEY,
  accessKeyId: process.env.AMAZON_ACCESS_KEY,
  region: "eu-north-1",
});

const s3 = new aws.S3();

const uploader = multer({
  storage: multerS3({
    s3: s3,
    bucket: "shopitemimages",
    acl: "public-read",
    contentType: multerS3.AUTO_CONTENT_TYPE,
    metadata: function (req, file, cb) {
      cb(null, { fieldName: "lets see what we want as fieldvalue" });
    },
    key: function (req, file, cb) {
      cb(null, Date.now().toString());
    },
  }),
});

module.exports = uploader;

If it's use this in an own route like below, it works like a charm.

const express = require("express");
const router = express.Router();
const multer = require("multer");

const uploader = require("../services/file-upload");

const singleUpload = uploader.single("file1");

router.post("/image-upload", (req, res) => {
  singleUpload(req, res, function (err) {
    console.log(req.file);
    res.json({ "image-url": req.file.location });
  });
});

module.exports = router;

However, when I try to use the MVC structure to use it together with another middleware, below code works for the upload to Mongodb, but not for S3.. it says success but nothing is uploaded.

This is the route:

const express = require("express");
const router = express.Router();
const multer = require("multer");
const upload = multer();


const shopController = require("../controllers/shop");

router.post(
  "/shop/create/:shopId",
  upload.single("file1"),//this seems to be needed, otherise I can't parse the file for mongodb upload
  shopController.createShopItem //controller doing the actual work
);

This is the controller i'm trying to use in above route:

const ShopItem = require("../models/shopitem");
const uploader = require("../services/file-upload");
const singleUpload = uploader.single("file1");


exports.createShopItem = (req, res, next) => {
  const file = req.file;
  const title = req.body.title;
  const price = req.body.price;
  const description = req.body.description;
  const location = req.body.location;
  const user = "OrreSnorre";

  if (
    file.mimetype != "image/jpeg" &&
    file.mimetype != "image/jpg" &&
    file.mimetype != "image/png"
  ) {
    next(new Error("invalid file type"));
  }
//this is the part where I try to upload to S3
  singleUpload(req, res, (err) => {
    console.log("iwas here");
    console.log(req.file);
    return res.json({ "image-url": req.file.location });
  });
  const newItem = new ShopItem({
    title: title,
    price: price,
    description: description,
    location: location,
    user: user,
  });

  newItem
    .save()
    .then((res) => console.log("saved"))
    .catch((err) => {
      const error = new Error(err);
      error.httpStatusCode = 500;
      return next(error);
    });
};

Any suggestions what I'm failing to understand?

I'v spent a few days hobby work on this.. let's see if someone is a lot quicker ;-)

Best regards, Oscar

Oscar Ekstrand
  • 581
  • 1
  • 4
  • 13

1 Answers1

1

After testing your codes i noticed what you are doing wrong, which are;

  1. const upload = multer(); creates the instance of multer without passing parameters(like the bucketID and the s3 credentials) to it, so nothing is going to happen when you try to use the instance as you tried to use it here upload.single("file1"). Instead you should call the middleware in the route as well.
  2. you declared singleUpload without calling it anywhere in your controller.

So I made some few modifications to your code, although with google GCS because I dont have S3 account to test with, but am confident it will work for s3 as well, and you can also create GCS account to test as well.

controllers/shop.js

//const ShopItem = require("../models/shopitem");
 const uploader = require("../services/file-upload");
 const singleUpload = uploader.single("file1");


exports.createShopItem = (req, res, next) => {
  const file = req.file;
  const title = req.body.title;
  const price = req.body.price;
  const description = req.body.description;
  const location = req.body.location;
  const user = "OrreSnorre";
  console.log("file.mimetype-->",file.mimetype);
  if (
     file.mimetype != "image/jpeg" &&
     file.mimetype != "image/jpg" &&
     file.mimetype != "image/png"
  ) {
    next(new Error("invalid file type"));
  }
    console.log("----------------------");
    console.log("if you can see this location: ",location);
    console.log("that means you can store into your mongodb");
    console.log("-----------------------");
    return res.json({ "image-url": req.file.location });

};

services/file-upload.js

const aws = require("aws-sdk");
const multer = require("multer");
const multerS3 = require("multer-s3");
const multerGoogleStorage =require('multer-cloud-storage');

require("dotenv").config();

aws.config.update({
 secretAccessKey: process.env.AWS_ACCESS_KEY,
 accessKeyId: process.env.AWS_ACCESS_KEY_ID,
 region: "eu-north-1",
});

const s3 = new aws.S3();

const uploader_ = multer({
   storage: multerS3({
    s3: s3,
    bucket: "shopitemimages",
    acl: "public-read",
    contentType: multerS3.AUTO_CONTENT_TYPE,
    metadata: function (req, file, cb) {
     console.log("this is the file",file);
     cb(null, { fieldName: "lets see what we want as fieldvalue" });
    },
    key: function (req, file, cb) {
     cb(null, Date.now().toString());
    },
  }),
});
const uploader = multer({
  storage: multerGoogleStorage.storageEngine({
   autoRetry: true,
   bucket: 'buck-name',
   projectId: 'projectid',
   keyFilename: 'pathtokeyfile.json',
   filename: (req, file, cb) => {
       cb(null, `/${Date.now()}${file.originalname}`);
       console.log(file);
    // output = output.replace(/{%DESCRIPTION%}/g, product.description);
   }
 }),
});

module.exports = uploader;

route.js

 const express = require("express");
 const router = express.Router();
 const multer = require("multer");
 // const upload = multer();


 const shopController = require('./controllers/shop');
 const uploader = require("./services/file-upload");
 router.post(
   "/shop/create/:shopId",
   uploader.single("file1"),// I changed this to object from your middleware
   shopController.createShopItem //controller doing the actual work
 );
module.exports = router;

server.js

const express = require("express");
const http = require("http");
const port = process.env.PORT || 3000;
//setup app & its routes
const app = express();
const routes = require('./route');
app.use(routes);

//start http server
const httpServer = http.createServer(app);
httpServer.listen(port);

let serviceName ="Upload service"
console.log(`[${serviceName}] http server listening at port ${port}`);
akisoft
  • 503
  • 6
  • 12