I'm trying to upload images as files using multer in my project, but I don't know why onCreate request in backend in the console I see on req.files: []; (empty array).
Here is where I create an element:
const [imagesColors, setImagesColors] = useState([{image: [], color: ''}])
const createProduct = (e) => {
e.preventDefault ();
const data = new FormData()
data.append("name", name)
data.append("description", description)
data.append("processor", processor)
data.append("ram", ram)
data.append("storage", storage)
data.append("imagesColors", imagesColors)
data.append("price", price)
data.append("type", type)
console.log(data)
fetch ('http://localhost:5000/products/create', {
method: 'POST',
body: data
})...
const handleInputChangeColor = (e, index) => {
const { name, value } = e.target;
const list = [...imagesColors];
list[index][name] = value;
setImagesColors(list);
};
const handleInputChangeImage = (e, index) => {
const name = e.target.name;
const file = e.target.files;
const list = [...imagesColors];
list[index][name] = file;
setImagesColors(list);
};
return (
...
{imagesColors.map((x, i) => {
return (
<div className="box">
<label htmlFor="file" className="file--Input--Container">
<input
type="file"
id="file"
multiple
name="image"
className="file--Input"
filename="imageFile"
placeholder="Product Image"
onChange={e => handleInputChangeImage(e, i)}
/>
<div className="file--Label--Container">
<FaCloudUploadAlt className="upload--Icon"/> Upload Images
</div>
</label>
<select
onChange={e => handleInputChangeColor(e, i)}
value={x.color}
name="color"
defaultValue=""
>
<option selected value="">Color</option>
<option value='#4f5b66'>Space-gray</option>
<option value='#a7adba'>Silver</option>
<option value='#FFFFFF'>White</option>
<option value='#F63204'>Red</option>
<option value='#000000'>Black</option>
<option value='#0095CB'>Pacific-Blue</option>
</select>
<div className="btn-box">
{imagesColors.length !== 1 && <button onClick={() => handleRemoveClick(i)}>Remove</button>}
{imagesColors.length - 1 === i && <button onClick={handleAddClick}>Add</button>}
</div>
</div>
);
})}
When I console.log the state imagesColors
it returns:
Here is the create function with multer function in backend:
const imageStorage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, "../../shpJS/frontend/src/styles/images")
},
filename: (req, file, callback) => {
callback(null, file.name)
}
})
const uploads = multer({storage: imageStorage})
router.post('/create', uploads.array("imagesColors"), async (req, res) => {
console.log(req, ' asd')
try {
const data = {
name: req.body.name,
description: req.body.description,
processor: req.body.processor,
ram: req.body.ram,
storage: req.body.storage,
imagesColors: req.files,
price: req.body.price,
type: req.body.type,
likes: req.body.likes
}
let product = await productService.create(data)
res.status(201).json(product)
} catch (error) {
res.status(500).json({error: error})
}
})
And the result in console from request:
To send multiple files in the frontend using FormData API, you have to append those files one by one. You can append them to the same field, and that field will arrive as an array in the backend. If you want to send additional data along with each file, append that data to a different field in the same order.
In your case, it looks like this:
for (const imageAndColor of imagesColors) {
data.append('images', imageAndColor.image);
data.append('colors', imageAndColor.color);
}
And in the backend change uploads.array("imagesColors")
to uploads.array("images")
. Your images will be in req.files
and the colors in req.body.colors
. The orders of both arrays are guaranteed — the first image is the first element in both arrays, second image is second element and so on.