Getting errors while uploading (Buffer) image file to Cloudinary in Node JS

42 views Asked by At

When I try to upload an image file (buffer) to Cloudinary, I get some errors in my code.

const cloudinary = require("cloudinary").v2;

But when I store images in the MongoDB database, it's stored perfectly. I created a user registration process. When user input their information and try to register then a token will be generated and stored in the database. But before storing the user info in the database I want to store the user image file to Cloudinary and store its URL in the database. When I try to do this, I am getting errors. Here is my code...

Process register

const image = req.file;
    if (!image) {
      throw createHttpError(400, "Image file is required");
    }

    if (image) {
      if (image.size > 1024 * 1024 * 2) {
        throw createHttpError(
          400,
          "File is too large. It must be less than 2 MB"
        );
      }
    }

    // buffer
    const imageBufferString = req.file.buffer.toString("base64");
// create jwt
    const tokenPayload = {
      username,
      email,
      password,
      phone,
      address,
      image: imageBufferString,
    };

   const token = createJSONWebToken(tokenPayload, jwtActivisionKey, "10m");

Verify user

try {
    const token = req.body.token;
    if (!token) throw createHttpError(404, "Token not found!");
    try {
      // verify user and register
      const decoded = jwt.verify(token, jwtActivisionKey);
      if (!decoded) throw createHttpError(401, "Unable to verify user");
      // console.log(decoded);

      const userExists = await User.exists({ email: decoded.email });
      if (userExists) {
        throw createHttpError(
          409,
          "User with this email already exists. Please login"
        );
      }

     const image = decoded.image;   // declaring image from the token
      
      // if (image) {
      //   const uploadResult = cloudinary.uploader.upload_stream(
      //     image,
      //     { folder: "Mern" },
      //     function (error, result) {
      //       console.log(error, result);
      //     }
      //   );
      //   return streamifier.createReadStream(image).pipe(uploadResult);
      // }

      await new Promise((resolve) => {
        const uploadResult = cloudinary.uploader
          .upload_stream(
            { folder: "Mern", resource_type: "image" },
            (error, result) => {
              if (result) {
                console.log(result);
                return resolve(result);
              }
              console.log(error);
            }
          )
          .end(image);
        streamifier.createReadStream(image.buffer).pipe(uploadResult);
        // decoded.image = uploadResult.secure_url;
      });

      // and then create user
      const user = await User.create(decoded);

      return successResponse(res, {
        statusCode: 201,
        message: "User was registered successfully",
        payload: user,
      });
    } catch (error) {
      if (error.name === "TokenExpiredError") {
        throw createHttpError(401, "Token has expired");
      } else if (error.name === "JsonWebTokenError") {
        throw createHttpError(401, "Invalid Token");
      } else {
        throw error;
      }
    }
  } catch (error) {
    next(error);
    console.log(error.message);
  }

File Upload

const multer = require("multer");

const MAX_FILE_SIZE = 2097152; // 2mb -- 1024bytes * 1024kb * 2
const ALLOWED_FILE_TYPES = ["image/jpg", "image/jpeg", "image/png"];
const UPLOAD_USER_IMG_DIR = "public/images/users";

// for user image
const userStorage = multer.memoryStorage();

const fileFilterUser = (req, file, cb) => {
  if (!file.mimetype.startsWith("image/")) {
    return cb(new Error("Only image files are allowed"), false);
  }
  if (file.size > MAX_FILE_SIZE) {
    return cb(new Error("File size exceeds the maximum limit"), false);
  }
  if (!ALLOWED_FILE_TYPES.includes(file.mimetype)) {
    return cb(new Error("File type is not allowed"), false);
  }
  cb(null, true);
};

const uploadUserImage = multer({
  storage: userStorage,
  fileFilter: fileFilterUser,
});

User Router

userRouter.post(
  "/process-register",
  uploadUserImage.single("image"),
  isLoggedOut,
  validateUserRegistration,
  runValidation,
  handleProcessRegister
);
userRouter.post("/activate", isLoggedOut, handleActivateUserAccount);

console.log output... { message: 'Invalid image file', name: 'Error', http_code: 400 }

when I change the code, the different errors occured in the console... TypeError [ERR_INVALID_ARG_TYPE]: The "chunk" argument must be of type string or an instance of Buffer or Uint8Array. Received undefined or The "path" argument must be of type string or an instance of Buffer or URL. Received undefined or { message: 'empty file', name: 'error', http_code: 400 }

1

There are 1 answers

0
Habib Shah On

Finally, I solved the errors...

I got the errors because I tried to encode a binary image directly into a JWT. When I tried to do this, the payload became larger for binary data. Also, it's a problem to decode binary images (JWT cannot decode exact large binary data) from JWT and user verification. JWT is not suitable for large payloads. This might not only lead to size issues but also problems when decoding and handling the binary data. Then I applied some processes:

Step: 1

i. At first, upload the image to Cloudinary.

 // Utility function to upload to Cloudinary
const uploadToCloudinary = (bufferImage) => {
  return new Promise((resolve, reject) => {
    const stream = cloudinary.uploader.upload_stream(
      { resource_type: "image", visibility: "public" },
      (error, result) => {
        if (error) reject(error);
        else resolve(result);
      }
    );
    streamifier.createReadStream(bufferImage).pipe(stream);
  });
};

module.exports = uploadToCloudinary;

ii. Get the image URL: After uploading, Cloudinary provides a response that includes the URL of the uploaded image.

*Upload image from userController...

const image = req.file;
if (!image) {
  throw createHttpError(400, "Image file is required");
}    
// buffer
const bufferImage = image.buffer;
// uploading image to cloudinary and collect its URL to encode it to the JWT
const result = await uploadToCloudinary(bufferImage);
if (!result || !result.secure_url) {
  throw new Error("Failed to upload image to Cloudinary.");
}
const imageUrl = result.secure_url;

iii. Then encode the image URL in JWT (create a JWT that includes the image URL in its payload).

// create jwt
const tokenPayload = {
  username,
  email,
  password,
  phone,
  address,
  image: imageUrl,
};
const token = createJSONWebToken(tokenPayload, jwtActivisionKey, "10m");

Step: 2 Decode JWT and process user verification by decoding the token and extracting the image URL.

// verify user and register
  const decoded = jwt.verify(token, jwtActivisionKey);
  if (!decoded) throw createHttpError(401, "Unable to verify user");
  
  const imageUrl = decoded.image;
  console.log(imageUrl);

   // and then create user
  const user = await User.create(decoded);