Required request part 'file' is not present - Angular2 Post request - spring

I am trying to get my file upload functionality done using Angular2 and SpringBoot. I can certify that my java code for the file uploading working fine since I have tested it successfully using Postman.
However, when it comes to sending the file from Angular2 front end, I am getting the HTTP 400 response saying Required request part 'file' is not present.
This is how I send the POST request from Angular2.
savePhoto(photoToSave: File) {
let formData: FormData = new FormData();
formData.append('file', photoToSave);
// this will be used to add headers to the requests conditionally later using Custom Request Options
this._globals.setRequestFrom("save-photo");
let savedPath = this._http
.post(this._endpointUrl + "save-photo", formData)
.map(
res => {
return res.json();
}
)
.catch(handleError);
return savedPath;
}
Note that I have written a CustomRequestOptions class which extends BaseRequestOptions in order to append Authorization header and Content Type header. Content Type header will be added conditionally.
Following is the code for that.
#Injectable()
export class CustomRequestOptions extends BaseRequestOptions {
constructor(private _globals: Globals) {
super();
this.headers.set('X-Requested-By', 'Angular 2');
this.headers.append('virglk', "vigamage");
}
merge(options?: RequestOptionsArgs): RequestOptions {
var newOptions = super.merge(options);
let hdr = this._globals.getAuthorization();
newOptions.headers.set("Authorization", hdr);
if(this._globals.getRequestFrom() != "save-photo"){
newOptions.headers.set('Content-Type', 'application/json');
}else{
//request coming from save photo
console.log("request coming from save photo");
}
return newOptions;
}
}
This conditional header appending is working fine. The purpose of doing that is if I add 'Content-Type', 'application/json' header to every request, file upload method in Spring controller will not accept it. (Returns http 415)
Everything seems to be fine. But I get Required request part 'file' is not present error response. Why is that? I am adding that param to the form Data.
let formData: FormData = new FormData();
formData.append('file', photoToSave);
This is the Spring Controller method for your reference.
#RequestMapping(method = RequestMethod.POST, value = "/tender/save-new/save-photo", consumes = {"multipart/form-data"})
public ResponseEntity<?> uploadPhoto(#RequestParam("file") MultipartFile file){
if (file.isEmpty()) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage("DEBUG: Attached file is empty");
return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.NOT_FOUND);
}
String returnPath = null;
try {
// upload stuff
} catch (IOException e) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage(e.getMessage());
return new ResponseEntity<ErrorResponse> (errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<String>(returnPath, HttpStatus.OK);
}
EDIT - Adding the payload of the request captured by the browser
As you can see, the param "file" is available there.

Try to add
headers: {
'Content-Type': 'multipart/form-data'
},
to your
.post(this._endpointUrl + "save-photo", formData)

Change formData.append('file', photoToSave);
to              formData.append('file', this.photoToSave, this.photoToSave.name); and also add headers specifying the type of data you are passing to API, in your case it will be 'Content-Type': 'multipart/form-data'. Post the output here if it fails even after changing this.

Is there a chance that you're using zuul in a secondary app that is forwarding the request? I saw this with an update where the headers were stripped while forwarding a multi-part upload. I have a gatekeeper app which forwards requests using zuul to the actual service via a looking from eureka. I fixed it by modifying the url like this:
http://myserver.com/service/upload
to
http://myserver.com/zuul/service/upload
Suddenly the 'file' part of the upload header was no longer stripped away and discarded.
The cause, I suspect was a re-try mechanism which cached requests. On failure, it would re-submit the requests, but somehow for file uploads, it wasn't working properly.

To upload a file to the server, send your file inside a FormData and set content type as multipart/form-data.
export const uploadFile = (url, file, onUploadProgress) => {
let formData = new FormData();
formData.append("file", file);
return axios.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data;charset=UTF-8',
// other headers
},
onUploadProgress,
})
};
To handle file object, be careful with consumes attribute and #RequestPart annotation here.
#PostMapping(value = "/your-upload-path", consumes = "multipart/form-data")
public ResponseEntity<Object> uploadFile(#RequestPart("file") #Valid #NotNull #NotBlank MultipartFile file) {
// .. your service call or logic here
}

Related

Expo & SpringBoot Multipart Upload Problem

I have been trying for hours now, to upload a file and a JSON using multipart file upload. I use Expo React Native as the client and SpringBoot as the server.
I already tried many different versions. After reading into this a lot, this is how it should work:
In my Expo app I have this:
const formData = new FormData();
formData.append(
'document',
new Blob([JSON.stringify(json)], {
type: 'application/json'
}));
formData.append('file', {
uri: url,
type: data.type,
name
});
const xhr = new XMLHttpRequest();
xhr.open('POST', API_URL);
xhr.setRequestHeader('Authorization', 'Bearer ' + jwt);
xhr.onload = () => {
const response = JSON.parse(xhr.response);
console.log(response);
// ... do something with the successful response
};
xhr.onerror = e => {
console.log(e, 'upload failed');
};
xhr.ontimeout = e => {
console.log(e, 'upload timeout');
};
xhr.send(formData);
In my SpringBoot Backend I have this:
#PostMapping(value = "/api/upload")
public ResponseEntity<Void> uploadDocument(
#RequestPart("document") DocumentDTO document,
#RequestPart("file") MultipartFile file) {
// ... my business logic
}
Now without the document it would work, but as soon as I add the document I get this error:
o.z.problem.spring.common.AdviceTraits : Bad Request: Required request part 'document' is not present
As a workaround I will upload files as base64 encoded strings for now ... But I really don't understand why this doesn't work, because it should.
Similar issue I think:
https://github.com/facebook/react-native/issues/30623
Any help would be greatly appreciated.
Try your backend with this approach. (May try removing Blob in FE, just leave it as
json string)
Create a class to wrap both document and file.
#Data
public class FormDataModel {
private MultipartFile file;
private DocumentDTO document;
public void setDocument(String document) {
ObjectMapper mapper = new ObjectMapper();
// this requires try-catch block in fact
this.document = mapper.readValue(document, DocumentDTO.class);
}
}
Use #ModelAttribute at Controller
#PostMapping(value = "/api/upload", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<Void> uploadDocument(#ModelAttribute FormDataModel wrapper) {
// ... my business logic
}

How to send multipart request in angular 6?

I need to send a multipart request.
When I am submitting the form I am getting below error from backend,
Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported
I am able to hit from Advanced rest client, but facing issue with angular.
On backend side below is The REST endpoint.
#PostMapping("/createCIF")
public Response < Map < String, Object >> createCIF(
#RequestPart("actDocs") List < MultipartFile > actDocs,
#Valid #RequestPart("createCIFReq") CreateCIFReq createCIFReq,
HttpServletRequest request) throws URISyntaxException {
}
Below is the angular side code in component.ts file.
let formData = new FormData();
formData.append('actDocs', this.userInfoService.mulitPartFileArray);
formData.append('createCIFReq', JSON.stringify(this.userInfo));
this.userInfoService.createCif(formData)
.pipe(first())
.subscribe(
data => {
}
}
Angular side Service level code
createCif(formData): any {
return this.http.post<any>(this.url + 'createCIF',
formData)
.pipe(map(cif => {
return cif;
}));
}
I got stuck on this issue an entire day.
Angular seems to fail to set a correct content-type to the JSON part.
I managed to solve this by creating a Blob :
let formData = new FormData();
formData.append('actDocs', this.userInfoService.mulitPartFileArray);
formData.append(
'createCIFReq',
new Blob([JSON.stringify(this.userInfo)], {type: 'application/json'})
);
Hope it helps.

Post MultipartFile - Request Part not preset Error

I try to send an image from a ionic Front-End application through a post method to Back-End services in Spring boot
I have done this method that makes the post to the backend url with the image inside a FormData object:
uploadImageService(url: string, image: any) {
console.log('post service: upload Image', + url);
// Initiates a FormData object to be sent to the server
const fd: FormData = new FormData();
fd.append('file', image);
const xhr = new XMLHttpRequest;
console.log('form data file: \n' + fd.get('file'));
xhr.open('POST', url);
// Send the FormData
xhr.send(fd);
console.log(xhr.response);
return xhr.responseText;
}
// call this method:
this.webapiService.uploadImageService(this.globalDataService.getUrlMedium() 'riskcontrol/subir-imagen', this.selectedImage);
This is the spring boot method that collects this post:
#RequestMapping(method = RequestMethod.POST, value = "/subir-imagen")
public ResponseEntity handleFileUpload(#RequestParam("file") MultipartFile file) {
LOGGER.log(Level.INFO, "/Post, handleFileUpload", file);
String associatedFileURL = fileManagerService.storageFile(file);
return ResponseEntity.ok(associatedFileURL);
}
When I do the post of the image I get this error:
.w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present]
I have launched the petition through Postman and it has worked,
that's why I think the error is in the tyscript code.
The only difference I see between postman and the code, is that in the form-data, let mark the key as type file or type text, and I have chosen type file.
I tried to make the request post in another way:
const httpOptionsImages = {
headers: new HttpHeaders({
'Content-Type': 'multipart/form-data',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, PUT, POST, DELETE'
})
};
// function
uploadImageService(url: string, image: any): Observable<any> {
console.log('post service: upload Image', + url);
// Initiates a FormData object to be sent to the server
const fd: FormData = new FormData();
fd.append('file', image);
return this.http
.post(url, fd, httpOptionsImages);
}
// call to the function
this.webapiService.uploadImageService(this.globalDataService.getUrlMedium() + 'riskcontrol/subir-imagen', this.selectedImage)
.subscribe( result => {
console.log(result);
});
But in this way I got another error:
FileUploadException: the request was rejected because no multipart boundary was found
What am I doing wrong?
Is there any way to indicate to FormData that the key is of type file as in postman?
Add the image as a Blob follow Ionic tutorial
const imgBlob = new Blob([reader.result], {type: file.type});
formData.append('file', imgBlob, file.name);
In the readFile function the program utilizes the FileReader from the File API to read the file into an ArrayBuffer. The onloadend event is called as soon as the file is successfully read. The app then creates a FormData object, wraps the array buffer in a Blob and adds it to the FormData object with the name 'file'. This is the same name the server expects as request parameter.
Remove your 'Content-Type': 'multipart/form-data'.
Have you tried on using #RequestPart instead of #RequestParam for MultipartFile file.

Spring Boot multipart.support.MissingServletRequestPartException while sending formData via POST request in Angular

I tried everything, and I am constantly getting this error from Spring Boot
Resolved exception caused by Handler execution: org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present
This is my Angular code. NOTE: I am using Http (not HttpClient) for my POST request.
updateUserProfilePicViaHttp(userId: number, imageFile: any) {
let headers: Headers = new Headers()
headers.append('Content-Type', 'multipart/form-data;boundary=imageUpload');
let formData: FormData = new FormData()
formData.append('file', imageFile, imageFile.name)
return this.http.post(this.baseUrl + `user/${userId}/profile_pic`, formData, { headers: headers })
}
This is my Spring Boot code
#RequestMapping(value="/{userId}/profile_pic",
method = RequestMethod.POST,
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
#ResponseBody
public ResponseEntity<User> uploadProfilePic(#PathVariable("userId") Integer id, #RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes){
IUserDao userDao = (IUserDao) getDao();
User user = null;
try {
user = userDao.saveAndUpdateProfilePic(id, file.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
if( user != null)
return new ResponseEntity<>(user, HttpStatus.OK);
else
return new ResponseEntity<>(user, HttpStatus.NOT_FOUND);
}
I think that there is something wrong with my Angular code, because when I send an image via Postman everything works fine!
Postman image:
Thanks!
EDIT: For some reason I tried replacing #RequestParam("file") MultipartFile file with #RequestParam MultipartFile file in my function.
Still getting the same error
It seems that when Spring says
Required request part 'file' is not present
It associates name 'file' with reference name of MultiPartFile file in my Spring boot function, not with #RequestParam('file').
EDIT2: I listened to Ravat and modified my code a little bit.
How I got imageFile?
Explanation:
#ViewChild('fileInput') myFile: ElementRef reference from <input type="file" #fileInput>
This is what is imageFile in my Angular function.
imageFile = this.myFile.nativeElement.files[0]
But still, same error...
After 6 hours of googling and explaining to Duck Overflow what is the problem, I stumbled upon this.
And I just removed my header
headers.append('Content-Type', 'multipart/form-data;boundary=imageUpload');
Final code
updateUserProfilePicViaHttp(userId: number, imageFile: any) {
let formData: FormData = new FormData()
formData.append('file', imageFile, imageFile.name)
return this.http.post(this.baseUrl + `user/${userId}/profile_pic`, formData)
}
The Issue is in following code code
updateUserProfilePicViaHttp(userId: number, image: string) {...}
I don't know about angular, here you are sending string in an image variable
that definitely won't work because it needs to be a file.

Unsupported Media Type with Content-Type header?

I'm getting an inexplicable error when I try to post an element by using the API of my backend. The API returns an error with code 415, related to Media Type:
Failed to load resource: the server responded with a status of 415 ()
My backend returns me this error:
Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain;charset=UTF-8' not supported
EDIT: Error with Guru solution:
Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain' not supported
The annoying thing is that I have added this header to my request:
Content-Type: application/json
And the body is correctly parsed into JSON format.
I am using Angular 5, and my backend has been developed by using Spring.
Angular client code:
postProject(project: Project) {
const headers: HttpHeaders = new HttpHeaders();
headers.append('Content-Type', 'application/json');
return this.http.post('http://localhost:8443/projects', project.body(), {headers: headers})
.map(response => response);
}
where body method is:
body() {
const object = {
id: this.id,
name: this.name,
'num_execs': this.num_execs
};
return JSON.stringify(object);
}
Spring API code:
#RequestMapping(value = "", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> addLocation(#RequestBody Project project) {
return new ResponseEntity<>(esProjectService.save(project), HttpStatus.OK);
}
Where RequestMapping of the class is /projects:
#RestController
#RequestMapping("/projects")
public class ProjectResource {
#RequestMapping(value = "", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> addLocation(#RequestBody Project project) {
return new ResponseEntity<>(esProjectService.save(project), HttpStatus.OK);
}
... OTHER METHODS...
}
I've already gone through this, one way to solve it is to specify the type #RequestPart (value = "nameOfResource" and consumes = {"multipart/form-data"}
Do not forget to specify the name of Content in Angular.
I hope it helps.
Here is an example below:
RequestMapping(value = "/add", method = RequestMethod.POST, consumes = {"multipart/form-data"}, produces = "application/json")
public ResponseEntity<?> add(#RequestPart(value = "image", required = true) MultipartFile image,
#RequestPart(value = "team", required = true) #Valid Team team, BindingResult bResult) {
}
In angular 5 HttpHeaders is immutable. Therefore, you should use it like this
let headers = new HttpHeaders({
'Content-Type': 'application/json',
'X-XSRF-TOKEN': token
});
or
let headers = new HttpHeaders();
headers = headers.append('Content-Type', 'application/json');
headers = headers.append('X-XSRF-TOKEN', token);
Set the headers in this way and it should resolve your issue. I have put the sample code just to explain how you should add multiple headers. Don't put 'X-XSRF-TOKEN' if you don't require it.
I think the mistake comes from the fact that you want to consume application / json while you send plain / text.
I explain, in your service you specify
#RequestMapping(value = "", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
while in your front code you send plain / text
return JSON.stringify(object);
Just delete the JSON.stringify call and return the json object and all is well.
Change
body() {
const object = {
id: this.id,
name: this.name,
'num_execs': this.num_execs
};
return JSON.stringify(object);
}
to
body() {
const object = {
id: this.id,
name: this.name,
'num_execs': this.num_execs
};
return object;
}

Resources