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 }
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.
ii. Get the image URL: After uploading, Cloudinary provides a response that includes the URL of the uploaded image.
*Upload image from userController...
iii. Then encode the image URL in JWT (create a JWT that includes the image URL in its payload).
Step: 2 Decode JWT and process user verification by decoding the token and extracting the image URL.