I am trying to upload the image from React Frontend to Cloudinary. I wrote a backend API called upload image. when I try through Postman it works fine the files are uploaded to Cloudinary Server but when I try through my React it shows the file not received. It really helps if anyone go through this code I share tell me the mistake I am making and why my backend is not getting the file.
import DataUriParser from "datauri/parser.js";
import path from "path";
const getDataURI = (file) => {
const parser = new DataUriParser();
const extName = path.extname(file.originalname).toString();
return parser.format(extName, file.buffer);
};
export default getDataURI;
which I use in the route
import multer from "multer";
const storage = multer.memoryStorage();
const singleUpload = multer({ storage }).single("file");
export default singleUpload;
export const uploadImagesUrl = expressAsyncHandler(async (req, res) => {
try {
const field = req.params.field;
try {
const file = req.file;
if (!file) {
return res.status(400).json({ error: "No file received" });
}
const fileURI = getDataURI(file);
const imgUpload = await cloudinaryConfig();
const { cloud_name } = imgUpload.config();
const result = await imgUpload.uploader.upload(fileURI.content, {
folder: "JewelryMine",
});
const image = await Image.findByIdAndUpdate(process.env.IMAGE_CONFIGURATION_ID);
const image_url = `https://res.cloudinary.com/${cloud_name}/image/upload/${result.public_id}`;
const updateObj = {
$set: {
[`${field}.public_id`]: result.public_id,
[`${field}.image_url`]: image_url,
},
};
if (image) {
await image.updateOne(updateObj, options)
return res.json({ message: "File Uploaded" });
} else {
return res.status(404).json({ message: "Field not found" });
}
} catch (error) {
console.error(error);
res.status(500).json({ error: "Upload failed" });
}
//
} catch (error) {
res.status(500);
throw new Error("Server Error. Please try after sometime.");
}
});
form
<div className='mt-2'>
<form onSubmit={submitHandler}>
<div className='flex flex-col'>
<input type='file' accept='image/*' onChange={handleImage} required />
</div>
<div className='flex flex-col'>
<label >Preview</label>
<img src={imgPreview} required />
</div>
<div className='mt-4'>
<button type='submit'>Update</button>
</div>
</form>
</div>
handleChange and handle Submit Part
const [image, setImage] = useState(null);
const [imgPreview, setImgPreview] = useState("");
const [updateImageUrl] = useUpdateImageUrlMutation();
const handleImage = async (event) => {
setImage(event.target.files[0]);
const url = await URL.createObjectURL(event.target.files[0] || image);
setImgPreview(url);
};
const submitHandler = async (event) => {
event.preventDefault();
const formData = new FormData();
formData.append("file", image);
try {
const res = await updateImageUrl({ field, formData }).unwrap();
if (res) {
toast.success(res.message || res.data.message);
}
} catch (err) {
toast.error(err?.data?.message || err?.error || err?.data?.error);
}
};
Updating with an answer based on the chat discussion:
There were two issues:
Content-Type
didn't match the data being sent (it likely defaulted to application/json
) while the content was actually multipart/form-data
. This caused the server not to be able to parse the request correctly.body
in the RTK query was containing both the formData
and field
based on the call to updateImageUrl
inside handleSubmit
.The solution for 1) was to set formData: true
in the RTK query and that will allow the Content-Type
header to be set automatically to multipart/form-data
as part of the request to match the data being sent.
For 2) the value of the body
was set to data.formData
.
The resulting RTK query with the above changes applied:
updateImageUrl: builder.mutation({
query: (data) => ({
url: `${IMAGES_API_URL}/upload/${data.field}`,
method: "PUT",
formData: true,
body: data.formData,
// credentials: "include",
}),
invalidatesTags: ["Images"],
}),