I have a form data which consists of multiple images under the same product id.
import client from "@/lib/fetchWrapper";
export default async function addProductImage({
photo,
productId,
}: {
photo: File[];
productId: string;
}) {
if (!productId || typeof productId !== "string") {
throw new Error("productId must be a non-empty string.");
}
try {
const formData = new FormData();
formData.append("productId", productId);
if (!photo || photo.length === 0) {
throw new Error("No image files found.");
}
// Append each image to the FormData
for (let i = 0; i < files.length; i++) {
formData.append('images[]', file[i])
}
formData.forEach((value, key) => {
console.log(key, value);
});
const formDataObj = Object.fromEntries(formData.entries());
console.log(formDataObj)
const response = await client("v1/product-image", {
method: "POST",
body: formDataObj,
headers: {
'Content-Type': 'application/json',
},
});
return response;
} catch (error) {
console.error("Error uploading product images:", error);
throw error;
}
}
I tried uploading three images and all three images are displayed while consoling the formdata:
productId 5b1a763a-4867-4bd8-a47b-f6d1f4642863
addProductImage.ts:55 images[]
File {name: 'durga-puja-parvati-ganesha-hinduism-png-favpng-aFuv1A72aKarGGLjPzqwaBKy1_t.png',
lastModified: 1697095379428,
lastModifiedDate: Thu Oct 12 2023 13:07:59 GMT+0545 (Nepal Time),
webkitRelativePath: '', size: 48632, …}lastModified: 1697095379428lastModifiedDate: Thu Oct 12 2023 13:07:59 GMT+0545 (Nepal Time) {}name: "durga-puja-parvati-ganesha-hinduism-png-favpng-aFuv1A72aKarGGLjPzqwaBKy1_t.png"size: 48632type: "image/png"webkitRelativePath: ""[[Prototype]]: File
addProductImage.ts:55 images[] File {name: 'durga-puja-parvati-ganesha-hinduism-png-favpng-aFuv1A72aKarGGLjPzqwaBKy1_t.jpeg', lastModified: 1697090823881, lastModifiedDate: Thu Oct 12 2023 11:52:03 GMT+0545 (Nepal Time), webkitRelativePath: '', size: 15891, …}
addProductImage.ts:55 images[] File {name: 'drug copy.png', lastModified: 1687005222158, lastModifiedDate: Sat Jun 17 2023 18:18:42 GMT+0545 (Nepal Time), webkitRelativePath: '', size: 45617, …}
addProductImage.ts:59
But when I convert the the formdata into JavaScript object using const formDataObj = Object.fromEntries(formData.entries());
Only, productId
is displayed in the payload not the array of images.
The fetchwrapper
code:
export default function client(
endpoint: string,
{ body, ...customConfig }: any = {}
) {
const token =
localStorage.getItem("ecommerceToken") ||
sessionStorage.getItem("ecommerceToken");
const headers = { "content-type": "application/json", Authorization: "" };
if (token) {
headers.Authorization = Bearer ${token};
}
const config = {
method: body ? "POST" : "GET",
...customConfig,
headers: {
...headers,
...customConfig.headers,
},
};
if (body) {
config.body = JSON.stringify(body);
}
return fetch(${process.env.NEXT_PUBLIC_API_URL}/${endpoint}, config).then(
async (response) => {
if (response.ok) {
return await response.json();
} else {
const error = await response.json();
return Promise.reject(new Error(error?.message));
}
}
);
}
The error on the response part is:
{
"statusCode": 400,
"message": [
"productId must be a string",
"productId should not be empty"
],
"error": "Bad Request"
}
But, I still see a valid productId
in the payload.
2
Answers
This is probably a duplicate but the answers I found when searching ranged from incomplete to terrible, so here we go:
You cannot naively convert a FormData using
Object.fromEntries
because FormData can hold multiple entries for a given key but JS objects are LWW (Last Write Wins) for a given key. For example:Note that you’re appending the same key
images[]
over and over in your loop.Here’s a general purpose conversion function that will turn multiple entries at the same key into an array:
Mine is similar to Jared but I think JSON.parse is unnecessary since you have to convert it back to JSON string so it can be sent in http request.
Also JSON.parse cab be expensive to compute if you have large/nested object.