0
votes

I am new in ExpressJs and working on creating api for one of a dashboard created in reactjs. There is a form in a dashboard which is collecting some of information from the users like "title", "description" and "image". I have created an express server to collect that information and to save it into mongodb. For images What I have done is that, I am uploading image to Cloudinary and storing uploaded url and public_id into database.

So after following some of tutorials I have done something like this.

index.js

const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");

const db = require("./db");

// Api router import goes here
const sectionTypesRouter = require("./routes/section-types-router");

const app = express();
const apiPort = 3000;

app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
app.use(bodyParser.json());

db.on("error", console.error.bind(console, "MongoDB connection error:"));

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.use("/api", sectionTypesRouter);

app.listen(apiPort, () => console.log(`Server running on port ${apiPort}`));

Than, First I have create a file multer.js :

const multer = require("multer");

const storage = multer.diskStorage({
  destination: "public/uploads",
  filename: (req, file, cb) => {
    cb(null, file.fieldname + "-" + Date.now());
  },
});

const fileFilter = (req, file, cb) => {
  if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
    cb(null, true);
  } else {
    //reject file
    cb({ message: "Unsupported file format" }, false);
  }
};

const upload = multer({
  storage: storage,
  fileFilter: fileFilter,
});

module.exports = upload;

Below is my api router section-type-router.js :

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

const SectionTypesCtrl = require("../controllers/section-types-ctrl");

const router = express.Router();

router.post(
  "/section-type",
  upload.single("image"),
  SectionTypesCtrl.createSectionType
);
router.get("/section-types", SectionTypesCtrl.getSectionTypes);

module.exports = router;

This is the section-type-ctrl.js :

const SectionType = require("../models/section-type-model");
const fs = require("fs");
const path = require("path");
const cloudinaryUploader = require("../utils/cloudinaryUploader");

const createSectionType = async (req, res) => {
  const body = req.body;

  if (!body) {
    return res.status(400).json({
      success: false,
      error: "Required parameter are missing",
    });
  }
  cloudinaryUploader
    .uploads(req.file.path, "Section-Types")
    .then((result) => {
      const sectionType = new SectionType({
        title: body.title,
        description: body.description,
        image: {
          url: result.url,
          publicId: result.public_id,
        },
      });
      sectionType
        .save()
        .then(() => {
          return res.status(201).json({
            success: true,
            id: sectionType._id,
            message: "Section type created!",
          });
        })
        .catch((error) => {
          return res.status(400).json({
            error,
            message: "Section type not created!",
          });
        });
    })
    .catch((error) => {
      res.status(500).send({
        message: "failure",
        error,
      });
    });
};

module.exports = {
  createSectionType,
};

And lastly this is cloudinaryUpload.js :

const cloudinary = require("../config/cloudinary");

exports.uploads = (file, folder) => {
  return new Promise((resolve) => {
    cloudinary.uploader.upload(
      file,
      {
        resource_type: "auto",
        folder: folder,
      },
      (err, result) => {
        if (!err) {
          resolve({
            url: result.url,
            public_id: result.public_id,
          });
        } else {
          throw err;
        }
      }
    );
  }).catch((error) => {
    throw error;
  });
};

Now, everything is working properly. Images is uploading to the cloudinary and returned url and public_id is storing in database. But the problem is that image that I have uploaded is also upload on local directory public/uploads/. This will may create a storage issue while host a site. So Is there any best way to upload image directly to the cloudinary without creating a copy in local directory which also should work on production mode ?

1
There's a npm module for multer npmjs.com/package/multer-storage-cloudinary also i tried to google "express.js cloudinary upload without storing file on local drive" and found enough information which should help you to do that, try google too.Molda
Will it good while we host the application ? I means are you sure there will no problem if upload directly without storing to it locally ? I have also found to uploading image as button with upload_stream function. Will it works ?Kishan Bharda

1 Answers

1
votes

In your example, the file is being stored to public/uploads on your server because you're telling multer to do so via multer.diskStorage

As @Molda's comment above says, you can avoid this by using the multer-storage-cloudinary package to have Multer store the file in Cloudinary automatically.

Another possibility is to change how you're using Multer so it doesn't store the file anywhere, then take the uploaded file while it's in memory and pass it to Cloudinary's SDK as a stream.

There's an example of this in this blog post on the Cloudinary site: https://cloudinary.com/blog/node_js_file_upload_to_a_local_server_or_to_the_cloud

In your case, you can stop using multer.diskStorage, in favour of just using multer() then use streamifier or another library to turn the uploaded file into a stream, and pass that to cloudinary.uploader.upload_stream()