Posting a file and JSON data to Spring rest service - java

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
}

Related

How to send multipart form data with json value in Spring Boot

Hi there, I want to send to postman a body with json and an image in formd-data...
I save the form-data image in a s3 bucket, the entity has as an string attribute that is the link of the image
Here my spring boot controller
#PostMapping(consumes = { "multipart/mixed", "multipart/form-data" }, produces = MediaType.APPLICATION_JSON_VALUE)
public CharacterResponse createCharacter(#Valid #RequestBody CharacterRequest characterRequest, #RequestParam(value = "file", required = false) MultipartFile file) {
CharacterDto characterDto = mapper.map(characterRequest, CharacterDto.class);
CharacterDto createdCharacter = characterService.createCharacter(characterDto, file);
return mapper.map(createdCharacter, CharacterResponse.class);
}
I have already tried with #RequestParam and #RequestPart for the MultiPartFile...
I get this error:
"Content type 'multipart/form-data;boundary=--------------------------340232294957024834187036;charset=UTF-8' not supported"
From my point of view you could try this:
in method attributes use #RequestPart with values. And also you should use the same structure in your client (for ex. in postman for each part you should explicitly set content-type, auto content-type works not perfect. For keys you should use values from #RequestPart, and in values just put your payload)
#PostMapping(consumes={ MediaType.MULTIPART_FORM_DATA_VALUE },
produces=MediaType.APPLICATION_JSON_VALUE)
public CharacterResponse createCharacter(
#Valid #RequestPart("body") CharacterRequest characterRequest,
#RequestPart(value="file", required=false) MultipartFile file)
{
//code
}
Just using #RequestParam for both parameter should do the job.
#PostMapping("/api/path")
public CharacterResponse createCharacter(#Valid #RequestParam CharacterRequest characterRequest, #RequestParam(required = false) MultipartFile file) {
CharacterDto characterDto = mapper.map(characterRequest, CharacterDto.class);
CharacterDto createdCharacter = characterService.createCharacter(characterDto, file);
return mapper.map(createdCharacter, CharacterResponse.class);
}

Multi-Part Request

I have an application which handles Multipart Request in below format.
POST .... HTTP/1.1
. . .
Accept:multipart/form-data
...
---boundary123 Content-type:application/octet-stream content-Disposition:
form-data filenale="payload.txt" name="someuniquename"
...
[paylaod content](this is in xml format)
---boundary123 content-type:application/json content-Disposition:form-data
name="someuniquname1"
{
...
ID:"999"
}
---boundary123
and here is my my controller part.
#Restcontroller
Class A{
#RequestMapping(value = "/a/b/c", method = RequestMethod.POST, consumes=
MediaType.MULTIPART_FORM_DATA_VALUE,
produces=MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody static void MyController(#RequestParam("file")
List<MultipartFile> files) {
}
Is this controller can parse both parts by identifying content-type(xml and json, no order) if I am receiving single multi-part file.If not Can you suggest format of controller for the same.
Use following to get the FormData in your Controller.
RequestMapping(value = "/yourPath", method = RequestMethod.POST)
public #ResponseBody Object upload(MultipartHttpServletRequest request, HttpServletResponse response) throws IOException {
//Get your form fields...
final String ID= request.getParameter('ID');
//and so on......
//Get your files.
Iterator<String> iterator = request.getFileNames();
MultipartFile multipartFile = null;
while (iterator.hasNext()) {
multipartFile = request.getFile(iterator.next());
//do something with the file.....
}
}
The way to achieve this would be to use the part boundry name with the RequestPart annotation:
#Restcontroller
Class A {
#RequestMapping(
value = "/a/b/c",
method = RequestMethod.POST,
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public #ResponseBody void myController(#RequestPart("someuniquname") SomePojo xmlPart, #RequestPart("someuniquname1") SomeOtherPojo jsonPart) {
// ...
}
// ...
}

Receiving uploaded file from client , rest

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)

Empty List<MultipartFile> when trying to upload many files in Spring with ng-file-upload

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.

how to send empty value for MultipartFile

I'm using angularJs and spring 4.0,
My Controller code:
#RequestMapping(value = "/endpoint/add", method = RequestMethod.POST)
public #ResponseBody GenericFormResponse execute(
WebRequest wreq,
#RequestParam("epName") String epName,
#RequestParam("ipAddr") String ipAddr,
#RequestParam("useDefault") String useDefault,
#RequestParam("certFile") MultipartFile certFile) throws Exception {
.....................
}
my js(angualrJs) code:
var formData = new FormData();
formData.append("epName", $scope.epName);
formData.append("ipAddr", $scope.ipAddr);
formData.append("useDefault",$scope.defaultCert);
if(!$scope.defaultCert){
formData.append("certFile", upFile);
}
$http({
method: "POST",
url: "./service/endpoint/add",
data: formData,
transformRequest: angular.identity,
headers: {'Content-Type': undefined }
}).success(svcSuccessHandler)
.error(svcErrorHandler);
My problem is $scope.defaultCert=false the POST request is working fine, $scope.defaultCert = true i'm getting Bad request(400).
i tried below thing also,
if(!$scope.defaultCert){
formData.append("certFile", upFile);
}else{
formData.append("certFile", null);
}
How do i sent empty MultipartFile.
Thanks.
I created two services in controller, and two urls for both
#RequestMapping(value = "/endpoint/add-with-cert", method = RequestMethod.POST)
public #ResponseBody GenericFormResponse excecuteWithCert(
WebRequest wreq,
#RequestParam("epName") String epName,
#RequestParam("ipAddr") String ipAddr,
#RequestParam("useDefault") boolean useDefault,
#RequestParam("certFile") MultipartFile certFile) throws Exception {
LOGGER.debug("received request for endpoint creation with certificate");
GenericFormResponse response = new GenericFormResponse();
SessionManager sessionMgr = new SessionManager(wreq);
if(!sessionMgr.isLoggedIn()) {
response.setSuccess(false);
response.setGlobalErrorCode("not_logged_in");
return response;
}
...............
}
#RequestMapping(value = "/endpoint/add", method = RequestMethod.POST)
public #ResponseBody GenericFormResponse excecuteWithOutCert(
WebRequest wreq,
#RequestParam("epName") String epName,
#RequestParam("ipAddr") String ipAddr,
#RequestParam("useDefault") boolean useDefault) throws Exception {
LOGGER.debug("received request for endpoint creation without certificate");
...............
}
in js file:
var url = "./service/endpoint/add";
if(!$scope.defaultCert){
formData.append("certFile", upFile);
url = "./service/endpoint/add-with-cert";
}
$http({
method: "POST",
url: url,
data: formData,
transformRequest: angular.identity, headers: {'Content-Type': undefined }
}).success(svcSuccessHandler)
.error(svcErrorHandler);
I don't know is this correct approach or not, but it is fulfilled my requirement. working as i expect. Please suggest best answers.
You need to configure properly to enable multipart file upload in Spring 4.
Add the following line to application initializer class:
dispatcher.setMultipartConfig(
new MultipartConfigElement("/tmp", 25 * 1024 * 1024, 125 * 1024 * 1024, 1 * 1024 * 1024)
);
Here dispatcher is an instance of ServletRegistration.Dynamic
Refer this answer for more details.

Categories

Resources