I am trying to send an image to be stored in the backend. The image is stored in a react useState that is updated when the user changes the the input form and is sent to its django backend api. The api accepts other inputs, not just the image, that are strings. So to send the request I store everything in an useState and make the request in an async function. The api has been tested to work on postman, the problem is I don’t know too well how to make the request. The image is an optional parameter and therefore it works perfect just with the other inputs, without the image.
Previously, I ignored the image and went along with the other inputs to make the request, which worked perfectly. I did this by putting everything in an object. Although when I put the image in the same object it stops working. I looked around and I found out to send images I need to use the FormData constructor and so I did, but I encountered a few problems.
This is how my form component looks like:
export default function Component() {
const [ image, setImage ] = useState<File>();
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
console.log('file', file)
setImage(file);
}
const handleSubmit = async (e: any) => {
const dummy_info = {
name: "hello",
start_date: "2024-01-01"
}
e.preventDefault();
const formData = new FormData();
formData.append('name', info.name);
formData.append('start_date', info.start_date);
if (image) formData.append('event_picture', image);
const res = await apiService.post('<backend_api_url>', formData, {
"Content-Type": "multipart/form-data"
});
}
return (
<form onSubmit={handleSubmit}>
<input
// value={info.event_picture || ''}
className="self-center" id="image" type="file" accept="image/*"
onChange={handleImageChange}
/>
<button type="submit">Submit</button>
</form>
)
}
I have the following apiService object that I use to make these calls reliably, although I think this may be where one of the issues is:
const apiService = {
post: async function(url: string, data?: any, extraHeaders?: any): Promise<any> {
console.log('post', url, data);
try {
const token = await getAccessToken();
const headers = {
'Authorization': `Bearer ${token}`,
"Content-Type": "application/json",
'Accept': 'application/json',
};
if (extraHeaders) {
Object.assign(headers, extraHeaders);
}
return new Promise((resolve, reject) => {
fetch(`${process.env.NEXT_PUBLIC_API_HOST}${url}`, {
method: 'POST',
body: data,
headers: headers
})
.then(response => response.json())
.then((json) => {
console.log('Response:', json);
resolve(json);
})
.catch((error => {
reject(error);
}))
})
} catch (e) {
console.error('error fetching data', e);
}
},
}
When adding an image and making this request, I get as a response the following {detail: 'Multipart form parse error - Invalid boundary in multipart: None'}
I know the FormData object is not empty since I console.log it and it returns the values (for the image: ['event_picture', File]
)
note: I am using Nextjs and thank you in advance
2
Answers
For sending an Image to your server you have multiple options:
I do not recommend first and second way of doing it because it increases complexity in your project. first way increases your database size because of huge amount of long strings saves inside your tables.
the second way does not increase database size but you have to write more functionality for managing your images, adding meta data to your images, converting them to different sizes (ex 64×64, 128×128, 256×256, etc…), searching between your images, having private and public images, handing who can access the image and many more.
I strongly recommend using an external S3 server to handle all the above and using Ant design or any other front-end framework that makes the job of getting your image and uploading it to S3 server much easier.
Basically you upload your image into an AWS S3 server and you can send the link of that image to your Django backend if the image is publicly accessible or if your image is private you can simply send its id to your Django backend and store in database, whenever a user with access comes then you create a custom timer link for that user.
You can learn about Ant.design at this URL https://ant.design/
You can learn more about S3 on AWS at this URL https://aws.amazon.com/pm/serv-s3/
Still if you want to go from route one you can easily convert your image into Base64String and sending that string to your server like an input text.
Here is how you can do it: How can I convert an image into Base64 string using JavaScript?
or If you want to use the second way and uploading the image directly to your server you can see this link:
https://omarshishani.com/how-to-upload-images-to-server-with-react-and-express/
When uploading files, such as images, you need to be careful to handle the FormData object. In React/Next.js, it is important let the browser manage ‘content-type’ automatically.
Manually setting the
Content-Type
to"multipart/form-data"
can lead to issues, such as the "Invalid boundary in multipart" error you encountered, because the browser will not include the necessary boundary that separates the different parts of the form data.To resolve this issue, you need to remove the manual setting of the ‘content-type’ in
Component
andService
.Let me know if you have any other issues after considering my opinion.