How to upload a file using Gin + React? - reactjs

I want to upload a image file from React, doing so seems I need to store the image on the server and retrieve it from a POST request in order to modify it into a url path.
Using Gin Gonic http framework.
Here is the form in React for my POST request:
<form action="/upload" method="POST">
<label htmlFor="upload" onChange={onChange}>
<FontAwesomeIcon icon={faUserCircle} color="#3B5998" size="10x" className={`${pointer}`} />
<input type="file" hidden id="upload" name="upload" />
</label>
</form>
Here is my method I am calling in an onChange to send the request along with the image file:
uploadFile = (file) => {
const formData = new FormData();
formData.append('image', file);
axios({
method: 'post',
url: `${gamingApiBaseURL}/v1/upload-image`,
data: formData,
headers: { 'Content-Type': 'multipart/form-data' },
}).catch((error) => {
console.log('error storing image', error);
});
};
Here is my Go struct in order to bind the request
type ImageForm struct {
Image *multipart.FileHeader `form:"upload binding:"required"`
}
Lastly, the method that is supposed to get the image file and upload it to the server. Here is what I have so far. I am receiving an error saying http: no such file which is weird because in Postman I see the image key and the filename, I'm trying to make sense of how uploading an image works in Go.
func extractImage(c *gin.Context) {
// Using 'ShouldBind'
formFile, err := c.FormFile("image")
fmt.Println("Error", err)
if err != nil {
c.JSON(http.StatusBadRequest, err)
}
fmt.Println("form error", formFile)
}

I think this struct tag is wrong
type ImageForm struct {
Image *multipart.FileHeader `form:"upload binding:"required"`
}
Change to this (missing double quote after upload):
type ImageForm struct {
Image *multipart.FileHeader `form:"upload" binding:"required"`
}

Related

unable to post with file upload using axios react js

I am trying to post multi-part form data with file upload using React and Axios. I tried the same in Postman and this is working correctly. But in react, I get "Required request part 'file' is not present". My code:
const selectedFile = event.target.file.files[0].name;
const formData = new FormData();
formData.append('name',this.state.name);
formData.append('description',this.state.description);
formData.append('file', selectedFile);
// Here I am passing content type and token and Post request .
axios({
method: 'post',
url: 'http://localhost:8080/user/1/savecategory',
data: formData,
headers: {
'Authorization':`Bearer ${passToken}`,
'Content-Type':'multipart/form-data'
}
})
.then(
(response) => {
alert("Category Saved..!");
},
(error) => {
console.log(error);
alert("Failed..!");
}
);
You haven't add the file field to the FormData Object. you only passed the name of the file as the second parameter.
de definition of append method is like this
For regular form field
formData.append(name, value);
And for Field field you can add It using this syntax
formData.append(name, value, filename);
Here selectedFile contain the name of the file which is selected in the file input field
const selectedFile = event.target.file.files[0].name;
When you call formData.append('file', selectedFile); you are passing the name of the file as value of the form element with name file and not the file It self.
to pass the file It self you should perform It like this
const selectedFile = event.target.file.files[0];
After that you can call formData.append('file', selectedFile);
Learn more about FormData.append

Spring API request giving "Content type 'application/octet-stream' not supported" error, however request is successful when using Postman

I am trying to send an API request through my React frontend to Spring backend. When I send the request I get this error:
Could not resolve parameter [0] in private org.springframework.http.ResponseEntity com.example.bottlecap.controllers.BottlecapController.entryForm(com.example.bottlecap.domian.Bottlecap,org.springframework.web.multipart.MultipartFile): Content type 'application/octet-stream' not supported
However, when I set up the request using Postman it goes through fine. I presume there may be an issue with how I am setting up my FormData on the React end. However, I have had little luck figuring it out.
My API is supposed to recieve an object that holds data about my submission as well as an image that goes with the submission. In my Postman request, I am creating a form data that holds a JSON file that holds all the object data and a random image just for testing. As I said, the requets goes through fine with this. However, in the frontend code, I am parsing through the object data as Json and adding it to a FormData as well as adding the image to the FormData.
Here is my Spring Controller:
#RequestMapping(path ="/bottlecap/entry", method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_OCTET_STREAM_VALUE})
private ResponseEntity entryForm(#RequestPart("cap") Bottlecap cap, #RequestPart("file") MultipartFile image){
System.out.println(cap);
cap.toString();
System.out.println("Test");
return ResponseEntity.ok().build();
}
Here is my react Frontend form submission handler:
handleSubmit = event =>{
console.log(this.state.page);
console.log(this.state.page);
event.preventDefault();
const cap ={
"name":this.state.name,
"brand":this.state.brand,
"yearMade":parseInt(this.state.yearMade),
"country": this.state.country,
"description":this.state.description,
"yearFound":parseInt(this.state.yearFound),
"isAlcoholic":"true"
};
const stringCap = JSON.stringify({cap});
console.log(cap);
var formData = new FormData();
formData.append('cap', JSON.parse(stringCap));
formData.append('file',this.state.imageFile)
axios.post('http://localhost:8080/bottlecap/entry', formData, {headers:{'Content-Type':'multipart/form-data'}})
.then(res=>{
console.log(res);
console.log(res.data);
//window.location = "/success"
this.setState({pageDone:true})
this.setState({pageLoading:true})
})
}
Here is a screenshot of my Postman request if it may help.
Also here is the contents of the json file I am sending through on Postman, if it may help as well.
{"name":"post-test",
"brand":"post-test",
"yearMade":1000,
"country":"post-test",
"description":"post-test",
"yearFound":1000,
"isAlcoholic":"true"}
The last change I did was adding a header to the axios API request, but still no luck.
In postman, for parameter named cap, you're sending a .json file. But in your reactjs code, you're doing
formData.append('cap', JSON.parse(stringCap));
JSON.parse will create a javascript object which is not what your backend is expecting. You need to send it as a JSON file.
Not tested, but this might give you the idea.
const json = JSON.stringify(cap);
const blob = new Blob([json], {
type: 'application/json'
});
var formData = new FormData();
formData.append('cap', blob);
formData.append('file', this.state.imageFile)
axios.post('http://localhost:8080/bottlecap/entry', formData, {headers:{'Content-Type':'multipart/form-data'}})
.then(res=>{
console.log(res.data);
}
This is my fetch sample in Vue3, and it works thanks to you. Thanks!
let formData = new FormData();
formData.append('productJsonData', new Blob([JSON.stringify(productJsonObject)], {type: 'application/json'}));
formData.append('file', image); // This image comes from an <v-file-input> TAG
const response = await fetch(
`http://.../addProduct`,
{
headers: { 'Accept': 'application/json' },
method: 'POST',
body: formData
}
);
const responseData = await response.json();
if (!response.ok) {
console.log('REST error: [' + responseData.error + ']')
throw error;
}

react.js file not uploading spring server

In my project, I have Spring Boot in the back-end and React.js in the front.
My back-end is working fine, I know, because I have tested it with Postman.
In the front-end to upload file, I have a named SubmitAssignment, which looks like this:
state={file:''};
uploadFile = (e) =>{
e.preventDefault();
var formData = new FormData();
formData.append("file", this.state.file);
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://localhost:8080/uploadFile");
xhr.onload = function() {
console.log(xhr.responseText);
var response = JSON.parse(xhr.responseText);
if(xhr.status === 200) {
console.log("upload successful");
} else {
console.log("upload failed");
}
};
xhr.send(formData);
};
onInputChange = (e) =>{
this.state.file=e.target.value;
console.log(this.state.file);
};
render() {
return (
<div>
<h1>Please select file(s):</h1>
<form>
<input className="input-file" id="my-file" type="file" onChange={this.onInputChange}/>
<button onClick={this.uploadFile}>Upload</button>
</form>
</div>
);
}
But the problem is upload is failing every time. Maybe the reason is the path, not sure. I tried to console.log the path. And what I got is C:\fakepath\Screenshot (187).png
Now my question if it is because of path, how can I do it correctly(as far as I know browser doesn't allow it for security concern)?
Otherwise, what is the problem? How to solve it ?
The error in browser console :
POST http://localhost:8080/uploadFile 400
And,
{"timestamp":"2019-09-16T07:20:30.382+0000","status":400,"error":"Bad Request","message":"Required request part 'file' is not present","trace":"org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present\r\n\tat
.......
Here is the full error message.
If the REST is needed, for any reason :
#PostMapping("/uploadFile")
public UploadFileResponse uploadFile(#RequestParam("file") MultipartFile file) {
String fileName = fileStorageService.storeFile(file);
String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/downloadFile/")
.path(fileName)
.toUriString();
return new UploadFileResponse(fileName, fileDownloadUri,
file.getContentType(), file.getSize());
}
From what I could see, in onInputChange() you are assigning the target value this.state.file=e.target.value; (This has the file path not the actual file)
Instead change to below, Important !
this.state.file=e.target.files[0];
And some suggestions are, use Fetch Api to send post request rather than using Plain old Javascript
const formData = new FormData();
formData.append("file", this.state.file);
fetch('http://localhost:8080/uploadFile', {
method: 'POST',
body: formData
})
.then(success => console.log(success))
.catch(error => console.log(error));
In your Spring boot controller use #RequestPart("file")
#PostMapping("/uploadFile")
public UploadFileResponse uploadFile(#RequestPart("file") MultipartFile file) {
//Logic
}

How to Save Image in server

I am trying to save an image in database but when I do the following it saves with dataURL, but that URL starts with localhost, how can I prevent it? I used React for frontend.
uploadImage = (event) => {
var imgSize = event.target.files[0].size;
$('#img').attr('hidden', false);
if (imgSize < 1000000 ) {
this.setState({
image: URL.createObjectURL(event.target.files[0]),
imageSize: imgSize
});
document.getElementById("errImgUpload").innerHTML = "";
}
else {
document.getElementById("errImgUpload").innerHTML = "Maximum image size 1Mb";
}
}
<div className="form-group">
<label for="file-upload" className="custom-file-upload">
<span className="fa fa-upload"></span> Upload image
</label>
<input onChange={(event) => this.uploadImage(event)} name="file-upload" id="file-upload" type="file" accept="image/*" />
<span id="errImgUpload" className="text text-danger"></span>
</div>
The Blob is http://localhost:10002/b46e96f5-83ce-4d10-b668-2bd038721b5a, what is a blob?
URL.createObjectURL() creates a blob which is a binary representation of the file in the memory. It doesn't upload the file. I am not sure from where you got the code. You may want to read more about this at MDN.
Uploading requires a backend service to open an endpoint for you to send post data to. You need to use <input type='file'> tag to send the file as form data and set the service endpoint as the url in the form. Start with this article.
You need to post your image data with FormData, like this:
const formData = new FormData();
formData.append('image', files[0]);
fetch(url, {
method: 'POST',
body: data
}).then(...);
And blob is Binary Large Object, more detail you can find in MDN.

How to send image with React to AspNetCore

I am trying to get a file on my react application like so:
<form onSubmit={this.onSubmit}>
<label htmlFor="Images">Images:</label>
<input type="file" id="Images" onChange={this.onChangeImage} accept="image/png, image/jpeg"/>
<button type="submit" value="SubmitImage" onClick={this.onSubmit}>Submit</button>
</form>
And onSubmit looks like this:
onSubmit = (event) => {
event.preventDefault();
if(event.target.value === "SubmitImage"){
fetch("https://localhost:5001/api/projects/edit/image/" + this.props.projectId + "/add",
{
method: "PUT",
body: JSON.stringify({photo: this.state.Images[0]})
});
}
else{
return;
}
}
And I am trying to receive this on my backend like so:
[HttpPut]
[Route("projects/edit/image/{id}/{toDo}")]
public void EditProjectImage(string id, string toDo, IFormFile photo)
{
if(toDo == "Add")
{
var result = mContext.Projects.Single(project => project.Id.Equals(id));
}
}
(ignore the lack of logic inside the if statement, I am currently just trying to have this function run)
What am I doing wrong? I have tried using [FromBody], using formdata but nothing works.
Use FormData for sending files
var formData = new FormData();
formData.append("photo", this.state.Images[0]);
fetch("https://localhost:5001/api/projects/edit/image/" + this.props.projectId + "/add",
{
method: "PUT",
body: formData
});
And leave IFormFile photo as is. Model binder binds parameters from form data by default which is correct behavior in this case. Or you may add [FromForm] to this parameter which is effectively the same in this case.

Resources