I wanted to upload image and some more stuff with form from angular. This is VM object :
public class UserPictureVM {
private MultipartFile file;
private String type;
private String coverPosition;
And this is rest method :
#PostMapping("/user/upload-picture")
#Timed
public ResponseEntity<Void> uploadUserPicture(#RequestBody UserPictureVM userPicture){
This is Angular method that submit form :
saveCoverImage() {
const formData: FormData = new FormData();
formData.append('coverPosition', '0, 0');
formData.append('file', this.coverForm.file);
formData.append('type', 'cover');
this.profileService.uploadImages(formData)
.subscribe(res=> {
console.log(res);
})
}
But i get error :
Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;charset=UTF-8' not supported
You are submitting form with 3 request parameters. Because of that, you should change your controller method signature to this:
public ResponseEntity<Void> uploadUserPicture(
#RequestParam("coverPosition") String coverPosition,
#RequestParam("file") MultipartFile file,
#RequestParam("type") String type)
Related
I am working on a Spring Boot Project where I want to send JSON data and Multipart File (Image) in a single API call. For this, I referred- https://blogs.perficient.com/2020/07/27/requestbody-and-multipart-on-spring-boot/#:~:text=Usually%20we%20add%20%40RequestBody%20and,So%2C%20annotation%20should%20be%20altered.
My Controller is-
#PostMapping(value = "/create",consumes = {MediaType.APPLICATION_JSON_VALUE,MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<SuccessResponse<PostDto>> createPost(
#RequestPart("post") String post,
#RequestPart("image") MultipartFile file,
#RequestParam(name = "userid") Integer uid,
#RequestParam(name = "categoryid") Integer categoryId) {
log.info("Filename :" + file.getOriginalFilename());
log.info("Size:" + file.getSize());
log.info("Contenttype:" + file.getContentType());
//convert the post string to POJO
PostDto postDto=postService.getJson(post);
//Now create the post
PostDto newPost = this.postService.createPost(postDto, uid, categoryId, file);
SuccessResponse<PostDto> successResponse = new SuccessResponse<>(AppConstants.SUCCESS_CODE,
AppConstants.SUCCESS_MESSAGE, newPost);
return new ResponseEntity<>(successResponse, HttpStatus.OK);
}
I am using Postman for testing-
When I make the request, I get the following error (Note- I have set the error response in Spring Security as seen in the image.)
[nio-8085-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required part 'image' is not present.]
I tried another approach but it gives another error-
#PostMapping("/uploadimage/{postid}/{isUpdatingPost}")
public ResponseEntity<SuccessResponse<String>> uploadImage(#RequestParam(name="file") MultipartFile file, #PathVariable("postid") int postid, #PathVariable("isUpdatingPost")boolean isUpdatingPost){
String result=this.postService.uploadImage(file, postid, isUpdatingPost);
SuccessResponse<String> response=new SuccessResponse<>(AppConstants.SUCCESS_CODE,AppConstants.SUCCESS_MESSAGE,result);
return new ResponseEntity<>(response,HttpStatus.OK);
}
[Request processing failed: org.springframework.web.multipart.MultipartException: Current request is not a multipart request] with root cause
org.springframework.web.multipart.MultipartException: Current request is not a multipart request
I am not able to understand the problem in these approaches. I also set the content type to multipart/form-data in Postman and the consume parameter in #PostMapping, but still getting these errors.
Please help in finding the problem!
This worked for me.
For attaching single file with #RequestParam:
#PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> uploadImage(#RequestParam MultipartFile file) throws IOException {
// ...
}
For attaching file in DTO with #ModelAttribute:
public record FileDTO(
Integer id,
MultipartFile file) {
}
#PostMapping(value = "/2", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.IMAGE_JPEG_VALUE)
public ResponseEntity<?> uploadImage2(#ModelAttribute FileDTO dto) throws IOException {
// ...
}
BTW, you probably want use OpenAPI(Swagger UI) for manually testing your app, it's easier than using postman.
Reference article: https://www.baeldung.com/spring-file-upload
I am trying to send an uploaded file (FormData from Angular) and a string in the same HTTP POST request to backend (Java using Grizzly server and Ajax for REST services).
The problem is that I receive HTTP 400 Bad Request because the file is not correctly mapped:
jersey message: Can not construct instance of java.io.InputStream: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type
In Frontend I have a class named Form created using ng g class Form, which contains:
export class Form {
private file:FormData;
private bookName: String;
constructor(file:FormData, bookName: String) {
this.file = file;
this.bookName = bookName;
}
}
The HTTP POST method from Frontend is:
sendFormData() {
const form = new Form(this.testData, this.bookName);
this.pdfService.sendFormData(form).subscribe((res) => {
console.log(res);
});
}
Above this.testData has the type FormData and this.bookName is a string. They both contain the expected input values.
The pdfService.sendFormData is:
public sendFormData(form: Form) {
console.log("sending to " + this.baseUrl + "uploadFile")
return this.http.post(this.baseUrl + "uploadFile", form, { responseType: 'text' });
}
In Backend I have a class Form.java (the mapped class):
public class Form {
String bookName;
InputStream file;
... (getters & setters & constructor)
}
and the HTTP POST method is:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_HTML)
#Path("uploadFile")
public Response convertPdfToHtml(Form form) {
...
}
To get the mapped string I use: form.getBookName() and to get the mapped file I use: form.getFile().
As I said, the problem is that the file from Frontend is not correctly mapped on the InputStream from Backend.
What type should I use to map a FormData from Frontend to a type in Backend? Or what other implementations could I use to send a file and a string in the same POST request?
Thanks.
in POST method you should provide object of type FormData, and there
export class Form {
private file: File;
private bookName: String;
constructor(file:File, bookName: String) {
this.file = file;
this.bookName = bookName;
}
}
public sendFormData(form: Form) {
const formData = new FormData();
formData.append('bookName', form.bookName);
formData.append('file', form.file, form.file.name);
console.log("sending to " + this.baseUrl + "uploadFile")
return this.http.post(this.baseUrl + "uploadFile", formData, {responseType: 'text'});
}
and you'll get post with content-type multipart/form-data
I am sending to backend a file, this is the upload code:
export class FileUploadComponent {
#Input() multiple: boolean = false;
#ViewChild('fileInput') inputEl: ElementRef;
constructor(private http: Http) {}
upload() {
let inputEl: HTMLInputElement = this.inputEl.nativeElement;
let fileCount: number = inputEl.files.length;
let formData = new FormData();
if (fileCount > 0) { // a file was selected
for (let i = 0; i < fileCount; i++) {
formData.append('file[]', inputEl.files.item(i));
}
this.http
.post('http://localhost:8080/upload', formData).toPromise().then(() => console.log('success')).catch(() => console.log('error'));
}
}
}
Now on the backendside I'd like to receive it via controller, but I don't know how to map the file property, following gives null's:
public #ResponseBody String handleFileUpload(#RequestBody MultipartFile file)
Your method signature is incorrect.
#RequestBody annotation parameters maps to the HTTP request body.
#RequestParam annotation parameters maps to the specific Servlet request parameters.
Use the following:
public #ResponseBody String handleFileUpload(#RequestParam MultipartFile file)
If you are sending multiple, then use array:
public #ResponseBody String handleFileUpload(#RequestParam MultipartFile[[] file)
I have the following controller method for uploading multiple files at once, inspired by this blog post and answers to this question as well:
#RequestMapping(value = "/{user}/attachment", method = RequestMethod.POST)
#PreAuthorize(...)
public void upload(#PathVariable User user,
#RequestParam("file") List<MultipartFile> files) {
// handle files
}
However, the list of the files is always empty although request contains them.
If I add the third MultipartRequest parameter to the method:
public void upload(#PathVariable User user,
#RequestParam("file") List<MultipartFile> files,
MultipartRequest request)
I can see it contains my uploaded files correctly:
What might be the reason of empty List<MultipartFile>?
I'm using ng-file-upload to submit the files, but I don't think it is connected with the issue. Spring 4.2.4.
The problem was that ng-file-upload by default submits array of files using names file[0], file[1] etc. It is configurable with the arrayKey value when using Upload Service. Setting it to empty string forces the files to be sent under the same file key, which is correctly resolved with Spring and the #RequestParam("file") List<MultipartFile> contains all files that has been submitted.
Upload.upload({url: url, data: {file: arrayOfFiles}, arrayKey: ''})
Try to use #ModelAttribute like this:
#RequestMapping(value = "/{user}/attachment", method = RequestMethod.POST)
#PreAuthorize(...)
public void upload(#PathVariable User user,#ModelAttribute("uploadFile") FileUpload uploadFile) throws IllegalStateException, IOException {
List<MultipartFile> files = uploadFile.getFiles();
...
And create a class like:
public class FileUpload {
private List<MultipartFile> files;
public List<MultipartFile> getFiles() {
return files;
}
public void setFiles(List<MultipartFile> files) {
this.files= files;
}
}
That works for me, sending big 'email' object with multiple file attachments from UI to back-end:
Angular
sendEmailWithAttachments(taskId: string, template: string, email: any, modelConfig: any, files: any[]) {
let formData = new FormData();
formData.append('form', new Blob([JSON.stringify(email)], {type: 'application/json'}));
files.forEach(file => {
formData.append('files', file);
});
return this.$http({
method: 'POST',
data: formData,
url: this.baseUrl + '/' + taskId + '/email-with-attachment?template=' + template,
headers: {
'Content-Type': undefined
},
responseType: 'arraybuffer'
});
}
Java Spring
#RequestMapping(value = "{taskId}/email-with-attachment", method = RequestMethod.POST, consumes = MULTIPART_FORM_DATA_VALUE)
public void sendEmailWithAttachment(
#PathVariable String taskId,
#RequestParam String template,
#RequestParam("form") MultipartFile form,
#RequestParam("files") List<MultipartFile> files) throws IOException {
Map<String, String> parameters = new ObjectMapper().readValue(form.getInputStream(), HashMap.class);
System.out.println("taskId"+ taskId);
System.out.println("template"+ template);
System.out.println("files"+ files);
System.out.println("parameters"+ parameters);
}
for multiple files. do this in your javascript
//first add files to form data
var formData = new FormData();
for (let i = 0; i < files.length; i++) {
formData.append("images", files[i]);
}
//post files to backend e.g using angular
$http.post('upload', formData, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.then(function(response){
console.log("UPLOAD COMPLETE::=> ", response);
}, function (error) {
console.log(error);
});
Do this in your java
//your java method signature
#PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE )
public Response uploadImage(#RequestParam(value = "images") MultipartFile[] images){
}
I think that in the way you sent data from front, it can not bound with java.util.List. If you create a JSON data as request and you annotated your List with #RequestBody like:
#RequestMapping(value = "/{user}/attachment", method = RequestMethod.POST)
#PreAuthorize(...)
public void upload(#PathVariable User user,
#RequestBody List<MultipartFile> files) {
// handle files
}
this should work. Some info here.
I am building a Spring rest service for uploading a file. There is a form that consists of various field and one field for uploading a file. On submitting that form, I am sending a multipart form request i.e. Content-Type as multipart/form-data.
So I tried with below
#RequestMapping(value = "/companies", method = RequestMethod.POST)
public void createCompany(#RequestBody CompanyDTO companyDTO, #RequestParam(value = "image", required = false) MultipartFile image){
.................
But, the above didn't work. So for time being,i sent JSON data as String and forming Company Object from that String in rest service like
#RequestMapping(value = "/companies", method = RequestMethod.POST)
public void createCompany(#RequestParam("companyJson") String companyJson, #RequestParam(value = "image",required = false) MultipartFile image) throws JsonParseException, JsonMappingException, IOException{
CompanyDTO companyDTO = new ObjectMapper().readValue(companyJson, CompanyDTO.class);
.............................
Can't I send JSON data with #RequestBody without passing JSON as String?
Appending the values to the URL what u have been doing now using #RequestParam.
#RequestParam annotation will not work for complex JSON Objects , it is specifi for Integer or String .
If it is a Http POST method , use of #RequestBody will make the Spring to map the incoming request to the POJO what u have created (condition: if the POJO maps the incoming JSON)
create FormData() and append your json and file
if (form.validate()) {
var file = $scope.file;
var fd = new FormData();
fd.append('jsondata', $scope.jsonData);
fd.append('file', file);
MyService.submitFormWithFile('doc/store.html', fd, '', (response){
console.log(response)
});
}
//Service called in above
MyService.submitFormWithFile = function(url, data, config, callback) {
$http({
method : 'POST',
url : url,
headers : {
'Content-Type' : undefined
},
data : data,
transformRequest : function(data, headersGetterFunction) {
return data;
}
}).success(function(response, status, header, config) {
if (status === 200) {
callback(response);
} else {
console.log("error")
}
}).error(function(response, status, header, config) {
console.log(response);
});
};
// in your java part using ObjectMapper
//it is like string
fd.append('jsondata', JSON.stringify($scope.jsonData));
#Autowired
private ObjectMapper mapper;
#RequestMapping(value = "/companies", method = RequestMethod.POST)
public void createCompany(#RequestParam String jsondata,
#RequestParam(required = true) MultipartFile file){
CompanyDto companyDto=mapper.readValue(jsondata, CompanyDTO.class);
......
}
Use below code snippet:
#RequestMapping(value= "/path", method=RequestMethod.POST, produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseObject methodName(MyData input, #RequestParam(required=false) MultipartFile file) {
// To Do
}