hagopj13 / node-express-boilerplate

A boilerplate for building production-ready RESTful APIs using Node.js, Express, and Mongoose
MIT License
6.99k stars 2.05k forks source link

While using multer, erros crash server #228

Open AlanGreyjoy opened 1 year ago

AlanGreyjoy commented 1 year ago

I have noticed that when using multer, the server crashes when you throw new ApiError() and I am pulling my hair out trying to track it down.

Route

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'src/data/mmsTemp/');
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + '-' + file.originalname);
  },
});

const upload = multer({ storage });
router
  .route('/:api_key/messages/mms')
  .get(apiKey, controllers.messages.getMMSMessages)
  .post(
    apiKey,
    upload.single('file'),
    controllers.messages.sendMMS
  );

Controller

module.exports.sendMMS = async (req, res) => {
  if (!req.file) {
    throw new ApiError(httpStatus.BAD_REQUEST, 'Missing file')
  }
  const data = {
    ...req.body,
    file: req.file,
    apiKey: req.user.apiKey,
    apiSecret: req.user.apiSecret,
    domain: req.user.domain,
  };
  await cpaasService.messages.sendMMS(data)
  return shSuccess(res);
};

Stack

error: Error: Missing file
    at module.exports.sendMMS (C:\XXXXXXXX\src\controller\ucaas\messages.controller.js:44:11)
    at Layer.handle [as handle_request] (C:\XXXXXXXX\node_modules\express\lib\router\layer.js:95:5)
    at next (C:\XXXXXXXX\node_modules\express\lib\router\route.js:144:13)
    at done (C:\XXXXXXXX\node_modules\multer\lib\make-middleware.js:45:7)
    at indicateDone (C:\XXXXXXXX\node_modules\multer\lib\make-middleware.js:49:68)
    at Multipart.<anonymous> (C:\XXXXXXXX\node_modules\multer\lib\make-middleware.js:166:7)
    at Multipart.emit (node:events:527:28)
    at Multipart.emit (node:domain:475:12)
    at emitCloseNT (node:internal/streams/destroy:138:10)
    at processTicksAndRejections (node:internal/process/task_queues:82:21)
info: Server closed
[nodemon] app crashed - waiting for file changes before starting...
bhattiasad99 commented 1 year ago

I also used multer in my application, faced several problems but now its working fine for me, ill let you know how i used it.

import { Router } from "express";
import { auth } from "../middlewares/index.js";
import { addPlan } from "../controllers/training-plan/index.js";
import multer from "multer";
import { failure } from "../../utils/helpers/responses.js";
import { getDirName } from "../../utils/helpers/dirname.js";
import uniqid from "uniqid";
import { UNKNOWN_SERVER_ERROR } from "../../config/index.js";
import { getLastElementOfArray } from "./../../utils/helpers/utility-functions.js";
import path from "path";

var storage = multer.diskStorage({
  destination: function (req, file, cb) {
    try {
      cb(null, getDirName(import.meta.url)?.__dirname + "/");
    } catch (err) {
      return res.status(UNKNOWN_SERVER_ERROR).json({
        message: err.message,
        errorInstance: { ...err },
      });
    }
  },
  filename: function (req, file, cb) {
    try {
      // cb(null, Date.now() + uniqid() + path.extname(file.originalname));
      cb(
        null,
        "Answer" +
          "." +
          getLastElementOfArray(file.mimetype.split("/")[1].split("."))
      );
    } catch (err) {
      return res.status(UNKNOWN_SERVER_ERROR).json({
        message: err.message,
        errorInstance: { ...err },
      });
    }
    // cb(null, "Answer" + "." + file.mimetype.split("/")[1]);
  },
});

const upload = multer({ storage: storage });
const router = Router();

// Post Request
router.post("/add-plan", auth, upload.single("file"), addPlan);

export default router;

This is how my API looked like:

export default async (req, res) => {
  let CONSECUTIVE_EMPTY_VALUES = 10;
  try {
    let results = [];

    const workbook = xlsx.readFile(
      `${getDirName(import.meta.url)?.__dirname}/../../routes/${
        req.file.filename
      }`
    );
    const sheet = workbook.Sheets[workbook.SheetNames[0]];

    // Get column range from the sheet
    const range = xlsx.utils.decode_range(sheet["!ref"]);

    for (let c = range.s.c; c <= range.e.c; c++) {
      let column = [];
      let emptyCellsCount = 0; // Initialize empty cell count for current column
      for (let r = range.s.r; r <= range.e.r; r++) {
        let cell = sheet[xlsx.utils.encode_cell({ r: r, c: c })];
        let value = cell ? cell.v : "";
        if (value === "") {
          // If cell is empty, increment empty cell count
          emptyCellsCount++;
          if (emptyCellsCount > CONSECUTIVE_EMPTY_VALUES) {
            // If CONSECUTIVE_EMPTY_VALUES found, move to next column
            break;
          }
        } else {
          // If cell is not empty, reset empty cell count
          emptyCellsCount = 0;
        }
        column.push(value);
      }
      results.push(column);
    }

    return await handleCsvData(req, res, results);
  } catch (err) {
    return failure(req, res, UNKNOWN_SERVER_ERROR, "00008", err.message, {
      error: { ...err },
    });
  }
};

getDirName function:

import path from "path";
import { fileURLToPath } from "url";

const getDirName = (filepath) => {
  const __filename = fileURLToPath(filepath);
  const __dirname = path.dirname(__filename);
  return {
    __filename,
    __dirname,
  };
};

export { getDirName };