0
votes

I initially tried to save the image file sent to the server as s3 using multer-s3. However, req.file or req.files of multer-s3 middleware kept returning undefined. So, I gave up multer-s3 and tried to save the image using multer and s3Client, but it also failed. Is there a problem with the way I use multer?

The framework is express, and as I said in the title, I tried sending an image using the form-data body in Postman. Also, I tried without specifying Content-Type in postman's request header, but it was the same. The code I wrote in the project is as follows.


./app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var bodyParser = require('body-parser');
var cors = require('cors');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var { sequelize } = require('./models');

var app = express();

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.set('port', process.env.PORT || 3000);

app.use(logger('dev'));
// app.use(express.json());
// app.use(express.urlencoded({ extended: false }));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(cors());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter);

sequelize.sync({ force: false })
  .then(() => {
    console.log('connect to db');
  })
  .catch((err) => {
    console.error(err);
  });

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  res
    .status(404)
    .json({
      responseMsg: 'not correct http method or path',
    });
  // next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

app.listen(app.get('port'), () => {
  console.log(app.get('port'), 'waiting for client');
});

module.exports = app;


./routes/index.js

var express = require('express');
var router = express.Router();
var path = require('path');
var { upload } = require('../lib/upload');

var member = require("./member");
var admin = require("./admin");
var column = require("./column");

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express2' });
});

router.post('/uploadOne', upload.single('img'), (req, res) => {
    console.log(req.file);
    const image = req.file;
    if(image == undefined) {
        res
            .status(400)
            .json({
                responseMsg: 'no image'
            });
    } else {
        res
            .status(201)
            .json({
                responseMsg: 'success',
                file: req.file,
            });
    }
});

router.post("/admin/login", admin.isAdmin);
router.get("/admin/verify", admin.verifyToken);
router.post("/admin/body", admin.getBody);

router.use("/api/member", member);
router.use("/api/column", column);

module.exports = router;

./lib/upload.js

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

AWS.config.update({
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
    region: process.env.AWS_S3_REGION
});

const s3 = new AWS.S3();

let upload = multer({
    storage: multerS3({
        s3: s3,
        bucket: '************',
        contentType: multerS3.AUTO_CONTENT_TYPE, 
        acl: 'public-read-write',
        metadata: function (req, file, cb) {
            cb(null, {fieldName: file.fieldname});
        },
        key: (req, file, cb) => { 
            console.log("success???? " + file);
            cb(null, 'users/' + file.originalname);
        },
    }),
    limits: { fileSize: 12 * 1024 * 1024 },
});

exports.upload = upload;

package.json dependency

"dependencies": {
    "aws-sdk": "^2.865.0",
    "body-parser": "^1.19.0",
    "connect-multiparty": "^2.2.0",
    "cookie-parser": "~1.4.4",
    "cors": "^2.8.5",
    "debug": "~2.6.9",
    "dotenv": "^8.2.0",
    "ejs": "~2.6.1",
    "express": "~4.16.1",
    "http-errors": "~1.6.3",
    "jsonwebtoken": "^8.5.1",
    "morgan": "~1.9.1",
    "multer": "^1.4.2",
    "multer-s3": "^2.9.0",
    "multer-s3-transform": "^2.10.3",
    "mysql2": "^2.2.5",
    "sequelize": "^6.3.5",
    "sequelize-cli": "^6.2.0",
    "sharp": "^0.27.2"
  },
  "devDependencies": {
    "nodemon": "^2.0.6"
  }
1
so you want to use multer to get the file saved locally, then upload to amazon S3 using SDK?PK214
Either way is fine. I want to succeed whether I use the Multer module to save the image file sent to postman locally or to s3. @DarkripperJun

1 Answers

0
votes

There are 2 ways to do this, either you can use only multer or multer-s3.

For simplicity, I will show you the way using only multer.

Flow of processing as follow:

  1. Multer process and save to local
  2. You read from local, and upload to s3 using s3 SDK
    // Make "temp" directory as multer.diskStorage wont create folder
    fs.mkdir('./temp', { recursive: true }, (err) => {
      if (err) throw err;
    });
    const PORT = parseInt(process.argv[2]) || parseInt(process.env.PORT) || 3000;
    // Multer
    const storage = multer.diskStorage({
      destination: function (req, file, cb) {
        cb(null, './temp');
      },
      filename: function (req, file, cb) {
        let extArray = file.mimetype.split('/');
        let extension = extArray[extArray.length - 1];
        cb(null, new Date().getTime() + '.' + extension);
      },
    });
    
    const upload = multer({ storage: storage });
    
    const endpoint = new AWS.Endpoint(AWS_S3_HOSTNAME);
    const s3 = new AWS.S3({
      endpoint,
      accessKeyId: AWS_S3_ACCESSKEY_ID,
      secretAccessKey: AWS_S3_SECRET_ACCESSKEY,
    });
    
    // Get the uploaded file in local here
    const readFile = (path) =>
      new Promise((resolve, reject) =>
        fs.readFile(path, (err, buff) => {
          if (null != err) reject(err);
          else resolve(buff);
        })
    
    // Upload to AWS S3 here
    const putObject = (file, buff, s3) =>
      new Promise((resolve, reject) => {
        const params = {
          Bucket: AWS_S3_BUCKET_NAME,
          Key: file.filename,
          Body: buff,
          ACL: 'public-read',
          ContentType: file.mimetype,
          ContentLength: file.size,
        };
        s3.putObject(params, (err, result) => {
          if (null != err) reject(err);
          else resolve(file.filename);
        });
      });
      );
    
    app.post('/api/post', upload.single('imageFile'), async (req, res) => {
    readFile(req.file.path)
            .then((buff) =>
              // Insert Image to S3 upon succesful read
              putObject(req.file, buff, s3)
            )
            .then((results) => {
              // build url of the resource upon successful insertion
              const resourceURL = `https://${AWS_S3_BUCKET_NAME}.${AWS_S3_HOSTNAME}/${results}`;
              const doc = {
                comments,
                title,
                ts: new Date(),
                image: resourceURL, // Your URL reference to image here
              };
            res.status(200).json(doc)
            })
            .catch((error) => {
              console.error('insert error: ', error);
              res.status(500);
              res.json({ error });
            });
}