Upload multiple files with metadata in one POST request - spring

I want to upload multiple files with related metadata in a single POST request.
Here's my POJO for that:
#Getter
#Setter
#NoArgsConstructor
public class Attachment {
private MultipartFile file;
private String name;
private String description;
}
I tried to upload the file with Postman using formdata (i.e. multipart/form-data), but it throws an exception saying the parameter attachment is not found.
I tried #ModelAttribute as well, but it fetches a blank list. When I tried with #RequestPart it threw this error: "Required request part 'attachments' is not present"
Here's my controller method:
#RequestMapping(value = "attachments", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
#ResponseBody
public void addAttachment(#RequestPart List<Attachment> attachments) {
// do something
}
We can do this by sending a raw json with base64 encoded file, but I'd like to know how this can be done using multipart. I have followed many questions on StackOverflow, but none of the solutions did work.
Thanks :)

#Lakshmikant Deshpande
Unfortunately, it is currently not possible to upload multiple files with parameters in one request.
For details, please refer to source code 'RequestPartMethodArgumentResolver.resolveArgument()'.

Related

How can I receive multiple files with respective metadata with my spring controller in one request?

I am developing a java spring webApp with a typescript frontend and want to send several files with some additional informations which were added by the user in the frontend to the backend (with one request). So far I managed to implement the functionality such that I can send a single file and additional information in one request which then gets mapped into a respective object in the backend.
In the code below I would like to implement functionality such that all elements of the attachments array are sent in one go. Preferably the controller in the backend should directly map them to an array of the respective QuoteAttachment object.
My frontend DTO:
export type QuoteAttachment = {
file: File,
uploadedOn: Date,
};
My frontend post request:
async createAttachment(attachments: QuoteAttachment[], quoteReference: string) {
let formData: FormData = new FormData();
formData.append('file', attachments[0].file);
formData.append('uploadedOn', attachments[0].uploadedOn.toLocaleString());
formData.append('relatedQuote', quoteReference);
await this.http.performRequest('/attachment', {
method: HttpRequestMethod.Post,
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
});
}
My backend DTO:
#Getter
#Setter
public class QuoteAttachment {
private MultipartFile file;
private String uploadedOn;
private String relatedQuote;
}
My backend controller:
#Controller
#RequiredArgsConstructor
#RequestMapping("/attachment")
public class AttachmentController {
private final AttachmentService attachmentService;
private final AttachmentValidationService attachmentValidationService;
#ResponseBody
#PostMapping
public void uploadFileWithInfos(#ModelAttribute final QuoteAttachment quoteAttachment) {
System.out.println("Test");
}
}
I really played around a lot and tried many different variations but didn't arrive at a good solution. I hope someone has a good idea for an implementation solving the problem.
I think there is a lot of solutions you can do for this:-
solution:
you can send the file as file in angular and as a Multipart in Spring boot, beside the QuoteAttachment DTO but as a String of JSON (NOT normal JSON). Example answer mentioned here in this URL then in java after receiving a String not a QuoteAttachment model you can map that string to a model with this simple code
ObjectMapper objectMapper = new ObjectMapper();
QuoteAttachment quoteAttachment = objectMapper.readValue(quoteAttachmentAsString, QuoteAttachment.class);
Note: ObjecMapper imported from com.fasterxml.jackson.databind library.
solution:
Also you can add all your QuoteAttachment DTO properties to formData in angular and receive them as #RequestParam in java. Example answer here
My preferred solution is number 1.

org.springframework.http.InvalidMediaTypeException: Invalid mime type "XML;charset=ISO-8859-1": does not contain '/'

I am creating a REST API using SpringBoot(API1). which calls another Rest API using RestTemplate(API2).
API2, which I am calling require xml input and gives xml output.
XML output which API 2 sends back has a structure like below
<xml>
<IList>
<IDetails>
<Id>INC123</Id>
</IDetails>
<IDetails>
<Id>INC124</Id>
</IDetails>
</IList>
</xml>
The Pojo Class I have created are below ones:
#Data
#XmlRootElement(name="xml")
public class IResponse implements Serializable{
private static final long serialVersionUID = 1L;
#XmlElement(name="IList")
public IList iList;
}
#Data
#XmlRootElement(name="IList")
public class IList implements Serializable{
#XmlElement(name="IDetails")
public List<IDetails> iDetails;
}
#Data
#XmlRootElement(name="IDetails")
public class IDetails implements Serializable{
#XmlElement(name="Id")
public String id;
}
The call I am doing :
ResponseEntity<IList> response = restTemplate.exchange(urlTemplate, HttpMethod.POST, request, IList.class);
url Template is the API2 that I am calling.
When restTemplate.exchange is executed, I get status code if 200 Ok, along with error Invalid mime type "XML;charset=ISO-8859-1": does not contain '/' . When I debugged the code .I found this is being thrown by responseExtractor.extractData(response) in package org.springframework.web.client; and doExecute method.
The headers I am sending are
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_XML_VALUE);
headers.add(HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.name());
The API is getting executed successfully, Reason being backend task that the API does is occurring when I executed my flow, but the response I get is Invalid mime type "XML;charset=ISO-8859-1": does not contain '/'.
Can Anyone help me please.
First, mime type is always has the view type/subtype.
I see you use Spring's HttpHeaders and MediaType. Looks like the class MediaType is not from Spring's package, check this. Possibly, in you 'wrong' imported class MimeType the value of MediaType.APPLICATION_XML is not application/xml but XML.
You can check it manually by setting the value application/xml.
2nd observation: if your headers already have ACCEPT header, it will added (will be at least 2 ACCEPT headers). If you want use one header, use .set instead .add.

SpringBoot does not get MultipartFile from Post request

I am trying to upload a multipart file in SpringBoot, But when I make a post request with the image in the body SpringBoot does not get the image file.
Here is a simplified sample of my code which presents the above problem (PostImage method returns -2):
#PostMapping("/Users/Image")
int PostImage(#RequestBody MultipartFile Image){
if( Image == null ) return -2;
return 0;
}
For the post request I tried both a REST client, and making the request from the frontend.
Any help will be greatly appreciated since I am now starting to learn SprinBoot.
REST client screenshot:
Edit
By changing the #RequestBody to #RequestParam("file"), and making the field name "file" on my frontend application (angular) I was able to make the request. I haven't succeed to POST with REST client though.
For your #RestController method, try #RequestParam MultipartFile Image. For your MVC #Controller, I suggest you define a form:
#NoArgsConstructor
#Getter #Setter
#EqualsAndHashCode(callSuper = false) #ToString #Log4j2
public class Form {
private MultipartFile file;
}
And do something like:
#RequestMapping(method = { POST }, value = { "/Users/Image" })
public String PostImage(..., #Valid Form form, BindingResult result) {
try {
if (! result.hasErrors()) {
...
MultipartFile file = form.getFile();
...

Get request body as string/json to validate with a json schema- Spring boot REST API

I'm trying to validate JSON (passed by a client as a request body) before it is converted into a model in Controller method.
If validation passes then return nothing, let the process continue as it was (spring boot to convert JSON into a model marked as #RequestBody). Throw error in case validation fails (everit-org/json-schema).
I tried to two way:
Implement HandlerMethodArgumentResolver, but resolveArgument() doesn't give request body details as it is already read and stored in ContentCachingRequestWrapper.
NOTE: inputStream in ContentCachingRequestWrapper doesn't have any request body details.
Using spring Interceptor. But this doesn't help me to find request body type passed in the request. As JSON schema is different for each request.
Any other approaches I can try with?
I cannot add a comment ... so ...
What kind of validation do you need? If you only want to validate the fields like length of a string or range of a number and so on. I recommend you use #Validated on controller mehtod parameter, and model:
#NotNull
#Size(min = 32, max = 32)
private String id;
controller:
#PatchMapping
public Object update(#RequestBody #Validated User user, Errors errors) {
...
}
If there is something wrong, errors.hasErrors() will return true.
edit:
OK, I did some tests, in a filter :
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
ServletInputStream inputStream = httpServletRequest.getInputStream();
byte[] a = new byte[1024];
inputStream.read(a);
System.out.println(IOUtils.toString(a));
I got a json string (a piece of request body) :
{"template":"5AF78355A4F0D58E03CE9F55AFA850F8","bd":"" ...

Uploading files using the Spring Framework and jquery-upload-file plugin issue

I am having trouble uploading files via AJAX from my web-client to my Server. I am using the following jQuery library in the client-side to do the file upload: https://github.com/hayageek/jquery-upload-file
In the server-side, I'm using Spring Framework and I have followed the following Spring Tutorial to build my Service: https://spring.io/guides/gs/uploading-files/
At first, my server method looked like this (file was defined as #RequestParam):
#RequestMapping(value="/upload", method=RequestMethod.POST)
public #ResponseBody String handleFileUpload(#RequestParam("file") MultipartFile file){
//functionality here
}
but every time I submitted the Upload form I got a Bad Request message from the Server, and my handleFileUpload() method was never called.
After that, I realized the file was not being sent as a Request Parameter so I defined file as #RequestBody, and now my method looks like this:
#RequestMapping(value="/upload", method=RequestMethod.POST)
public #ResponseBody String handleFileUpload(#RequestBody("file") MultipartFile file){
//functionality here
}
Now handleFileUpload() is called every time the Upload form is submitted, but I am getting a NullPointerException every time I want to manipulate file.
I want to avoid submitting the form by default, I just want to do it through AJAX straight to the Server. Does anybody know what could be happening here?
you may try changing the signature of the method to
#RequestMapping(value="/upload", method=RequestMethod.POST)
public #ResponseBody String handleFileUpload(MultipartHttpServletRequest request){
Iterator<String> iterator = request.getFileNames();
while (iterator.hasNext()) {
String fileName = iterator.next();
MultipartFile multipartFile = request.getFile(fileName);
byte[] file = multipartFile.getBytes();
...
}
...
}
this works with jQuery File Upload in our webapp. If for some reason this does
not work for you, you may try to isolate the problem, by inspecting the HTTP
request issued by the jQuery File Upload (for example, with Fiddler), and debugging the response starting from Spring
DispatcherServlet.

Resources