Java, Rest API, multipart - java

I want to save an entity in DB. The entity has several fields and photo (photo I will save in byte[]). I wrote a RestController, but it hasn't worked.
Format JSON, I use postman, in form-data I add a file, in raw I put body and use JSON.
#RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = {"multipart/form-data"})
#ResponseBody
public void storeAd(#RequestPart("client") #Valid Client client, #RequestPart("file") #Valid MultipartFile file) throws IOException {
Client myClient = client;
byte[] s = file.getBytes();
int a = s.length;
}
I see error:
Resolved exception caused by handler execution:
org.springframework.web.HttpMediaTypeNotSupportedException:
Content type 'application/json' not supported
Request from postman1

You need to add "Content-Type : application/x-www-form-urlencoded" in header section.

Related

Parsing MultipartFile data to string in spring

I am trying to parse the MultipartFile data to string and return the string as response in java springboot. Can anyone suggest the right approach for this?
controller.java
#POST
#Path("/modelInfo")
#Produces({ "application/json" })
public Response getPretrainedModel(MultipartFile data) throws IOException {
String content = new String(data.getBytes(), StandardCharsets.UTF_8);
return Response.status(Response.Status.OK).entity(content).build();
}
file.json
{
"documents": [
{
"id": "1",
"text": "abc"
}
]
}
I am sending file.json in request body as multipart/form-data and I want to read the content of the file and store it as string.
If you are using Spring and Spring Boot, then you are not using the proper annotation. Spring does not support Jax-RS specification. Therefor, change your annotations for this one:
// #POST
// #Path("/modelInfo")
// #Produces({ "application/json" })
#PostMapping(value = "/modelInfo", produces = MediaType.APPLICATION_JSON_VALUE)
Then, to return an object, you can just return the object in the method:
#PostMapping(value = "/modelInfo", produces = MediaType.APPLICATION_JSON_VALUE)
public String getPretrainedModel(#RequestParam("file") MultipartFile data) throws IOException {
String content = new String(data.getBytes(), StandardCharsets.UTF_8);
return content;
}
Note:
Don't forget to add the annotation #RequestParam in your method parameter to get the uploaded file. The name file must be the name of the attribute uploaded by your POST request
By default, the HTTP Response is 200 when you don't tell Spring to send something else.
If you want to override that, annotate your method with #ResponseStatus

Combine file upload and request body on a single endpoint in rest controller

The UI for my webapp has the ability to either upload a file(csv), or send the data as json in request body. However either a file upload, or a json request would be present in the request and not both. I am creating a spring rest controller which combine file upload and also accepts the request json values as well.
With the below endpoint tested from postman, I am not getting exception:
org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
#RestController
public class MovieController {
private static final Logger LOGGER = LoggerFactory.getLogger(MovieController.class);
#PostMapping(value="/movies", consumes = {"multipart/form-data", "application/json"})
public void postMovies( #RequestPart String movieJson, #RequestPart(value = "moviesFile") MultipartFile movieFile ) {
// One of the below value should be present and other be null
LOGGER.info("Movies Json Body {}", movieJson);
LOGGER.info("Movies File Upload {}", movieFile);
}
}
Appreciate any help in getting this issue solved?
Note: I was able to build two separate endpoint for file upload and json request, but that won't suffice my requirement. Hence I'm looking for a solution to combine both
Try something like:
#RequestMapping(value = "/movies", method = RequestMethod.POST, consumes = { "multipart/form-data", "application/json" })
public void postMovies(
#RequestParam(value = "moviesFile", required = false) MultipartFile file,
UploadRequestBody request) {
In RequestBody you can add the parameters you want to send.
This will not send the data as JSON.
Edit:- I forgot to add the variable for the Multipart file and I mistakenly used the RequestBody which is reserved keyword in spring.
Hope it helps.
I would suggest to create two separate endpoints. This splits and isolates the different functionality and reduces the complexity of your code. In addition testing would be easier and provides better readability.
Your client actually has to know which variable to use. So just choose different endpoints for your request instead of using different variables for the same endpoint.
#PostMapping(value="/movies-file-upload", consumes = {"multipart/form-data"})
public void postMoviesFile(#RequestPart(value = "moviesFile") MultipartFile movieFile ) {
LOGGER.info("Movies File Upload {}", movieFile);
}
#PostMapping(value="/movies-upload", consumes = {"application/json"})
public void postMoviesJson( #RequestPart String movieJson) {
LOGGER.info("Movies Json Body {}", movieJson);
}

How to modify the parameter name of MultipartFile in uploading?

I need to upload a MultipartFile to a third-party service via my own backend service. The parameter in the multipart form is 'nameA' but the third-party service need its param name is 'nameB'.
Normally I can solve it in two ways:
Change the param name of frontend to 'nameB'
Change the param name of MultipartFile to 'nameB' in backend service.
But I cannot change the frontend now, so I want to finger out how to modify the param name of MultipartFile in backend service.
The controller of backend service is:
#PostMapping("/url")
public Response method(#RequestParam("nameA") MultipartFile file) {
return Service.method(file);
}
In Feign Client for uploading file to third-party service:
#PostMapping(value = "/url1/url2", consumes = MULTIPART_FORM_DATA_VALUE)
Response method(#RequestParam("nameB") MultipartFile file);
However the use of specify the param with đŸ‘† #RequestParam doesn't work.
So does anyone know how to modify the param name of MultipartFile? Thanks a lot!
That is completely unrelated to your controllers spring annotations and instead depends on how you would upload that file to the 3rd party service. Since you mentioned uploading it, I assume you need to create a new HTTP multipart request in your backend service that would upload the file to the 3rd party service. When creating that request, you will be able to specify the name of the multipart part.
You can set a name of the MultipartFile in the FeignClient as you need, this is a sample from my project:
Đ¡ontroller API (receiving side):
#RestController
#RequestMapping("/files")
public class FilesController {
#PostMapping(path = "/upload")
#ResponseStatus(HttpStatus.CREATED)
public FileDescriptor upload(#RequestPart(value = "data") MultipartFile multipartFile) {
...
}
}
Feign client (sending side):
#FeignClient(value = "file-service", configuration = FeignConfig.class)
public interface ContentStorageFeign {
#ResponseBody
#PostMapping(value = "/files/upload", produces = MediaType.APPLICATION_JSON_VALUE)
FileDescriptor create(#RequestPart(value = "data") MultipartFile multipartFile);
}
And this is my FeignConfig:
#Configuration
public class FeignConfig {
#Bean
public Decoder decoder(ObjectFactory<HttpMessageConverters> messageConverters) {
return new ResponseEntityDecoder(new SpringDecoder(messageConverters));
}
#Bean
public Encoder encoder(ObjectFactory<HttpMessageConverters> messageConverters) {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
}
But if you need to create a new request(from a file received from somewhere) and rename this file before sending, this is another problem.

"status":415,"error":"Unsupported Media Type"

I have following controller:
#PostMapping(value = {"/test/set_timeout"})
#ResponseBody
public void setAlertTimeout(#RequestBody Long timeout) {
and I make following request in postman:
text from error:
{"timestamp":1495560868392,"status":415,"error":"Unsupported Media Type","exception":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'multipart/form-data;boundary=----WebKitFormBoundary9IbVq5JAKxCYUs7P;charset=UTF-8' not supported","path":"/test/set_timeout"}
What the reason of the problem and how to send request correct?
P.S.
If to use
public static class LongWrapper{
private long timeout;
public long getTimeout() {
return timeout;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
}
instead of Long and pass json({"timeout":"2"}) - it works correct
When reading json your content type should be application/json.
if you need to use application/json type and write below code in controller method :-
#RequestMapping(method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
public RestResponse save(#RequestParam("timestamp") String timestamp, #RequestParam("status") String status) {
#RequestBody is used to deserialize JSON to POJO. If you need to submit form data to the controller try something like
#PostMapping(value = {"/test/set_timeout"})
#ResponseBody
public void setAlertTimeout(HttpServletRequest request) {
Long l = request.getParameter("timeout");
// continue
}
In your request you are sending the timeout property in a multipart/form-data body.
In postman, select option 'raw' for the body and set the content type to application/json. Then enter the following as body content:
{timeout: 4}
You should also set the consumes MediaType on your controller. (Although it could be that it automatically resolves to json) For example:
#PostMapping(value = {"/test/set_timeout"}, consumes = {MediaType.APPLICATION_JSON_VALUE})
There is no need to add a content-type header manually. You are overriding the value set by Postman. Just select form-data in POST request and send your request to see if it works.

How to process a multipart request consisting of a file and a JSON object in Spring restful service?

I have the following resource (implemented using Spring 4.05.RELEASE) which accepts a file and a JSON object:
(P.S. activityTemplate is a serializable entity class)
...
#RequestMapping(value="/create", method=RequestMethod.POST)
public #ResponseBody ActivityTemplate createActivityTemplate(
#RequestPart ActivityTemplate activityTemplate, #RequestPart MultipartFile jarFile)
{
//process the file and JSON
}
...
and this is the form I am testing from:
<form method="POST" enctype="multipart/form-data"
action="http://localhost:8080/activityTemplates/create">
JSON: <input type="text" name="activityTemplate" value='/* the JSON object*/'><br />
File to upload: <input type="file" name="file">
<input type="submit" value="Upload">
</form>
and this is the error that I get:
There was an unexpected error (type=Unsupported Media Type, status=415).
Content type 'application/octet-stream' not supported
So how should I make the resource accept the JSON object as part of the multipart request, or should I be sending the form in a different way?
This took me two days to work for me!
client (angular):
$scope.saveForm = function () {
var formData = new FormData();
var file = $scope.myFile;
var json = $scope.myJson;
formData.append("file", file);
formData.append("ad",JSON.stringify(json));//important: convert to string JSON!
var req = {
url: '/upload',
method: 'POST',
headers: {'Content-Type': undefined},
data: formData,
transformRequest: function (data, headersGetterFunction) {
return data;
}
};
Spring (Boot):
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public #ResponseBody
Advertisement storeAd(#RequestPart("ad") String adString, #RequestPart("file") MultipartFile file) throws IOException {
Advertisement jsonAd = new ObjectMapper().readValue(adString, Advertisement.class);
//do whatever you want with your file and jsonAd
Hope this should help you. You need to set the boundary in your request to inform the HTTP Request.
is simple; A brief introduction to the multipart format can be found in the below link
HTML 4.01 Specification for multipart
The following example illustrates "multipart/form-data" encoding.
If the Json Object is "MyJsonObj" , and file that need to be send is "myfile.txt", the user agent might send back the following data:
Content-Type: multipart/form-data; boundary=MyBoundary
--MyBoundary
Content-Disposition: form-data; name="myJsonString"
Content-Type: application/json
MyJsonObj //Your Json Object goes here
--MyBoundary
Content-Disposition: form-data; name="files"; filename="myfile.txt"
Content-Type: text/plain
... contents of myfile.txt ...
--MyBoundary--
or if your files is of type image with name "image.gif" then,
--MyBoundary
Content-Disposition: file; filename="image.gif"
Content-Type: image/gif
Content-Transfer-Encoding: binary
...contents of image.gif...
--MyBoundary--
You specify boundary in the Content-Type header so that the server knows how to split the data sent.
So, you basically need to select a boundary value to:
Use a value that won't appear in the HTTP data sent to the server like 'AaB03x'.
Be consistent and use the same value all over the request.
You have not given the param names to your #RequestParts ?
public #ResponseBody ActivityTemplate createActivityTemplate(
#RequestPart("activityTemplate") ActivityTemplate activityTemplate, #RequestPart("file") MultipartFile jarFile)
{
//process the file and JSON
}
Note: do not forget to include the jackson mapper .jar (maps your Json to ActivityTemplate) file in your classpath.
Couldn't you change your
#RequestMapping(value="/create", method=RequestMethod.POST)
to
#RequestMapping(value="/create",
method=RequestMethod.POST, consumes ={"multipart/form-data"})
You can use #RequestPart from org.springframework.web.bind.annotation.RequestPart; It is used as Combining #RequestBody and file upload.
Using #RequestParam like this #RequestParam("file") MultipartFile file you can upload only file and multiple single data (key value ) like
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST, consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public void saveFile(
#RequestParam("userid") String userid,
#RequestParam("file") MultipartFile file) {
}
you can post JSON Object data and and File both using #RequestPart like
#RequestMapping(value = "/patientp", method = RequestMethod.POST, consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<?> insertPatientInfo(
#RequestPart PatientInfoDTO patientInfoDTO,
#RequestPart("file") MultipartFile file) {
}
You are not limited to using multipart file uploads directly as controller method parameters. Your form objects can contain Part or MultipartFile fields, and Spring knows automatically that it must obtain the values from file parts and converts the values appropriately.
Above method can respond to the previously demonstrated multipart request containing a single file. This works because Spring has a built-in HTTP message converter that recognizes file parts. In addition to the javax.servlet.http.Part type, you can also convert file uploads to org.springframework.web.multipart.MultipartFile. If the file field permits multiple file uploads, as demonstrated in the second multipart request, simply use an array or Collection of Parts or MultipartFiles.
#RequestMapping(value = "/patientp", method = RequestMethod.POST, consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<?> insertPatientInfo(
#RequestPart PatientInfoDTO patientInfoDTO,
#RequestPart("files") List<MultipartFile> files) {
}
Happy To Help...
The default content type is 'application/octet-stream'. Since you are uploading jar file and JSON the content type should be set in the #RequestMapping annotation as follows:
#RequestMapping(value="/create", method=RequestMethod.POST, headers="content-type=application/json,application/java-archive")
The error message indicates that there is no HttpMessageConverter registered for a multi-part/MIME part of content type: application/octet-stream. Still, your jarFile parameter is most likely correctly identified as application/octet-stream, so I'm assuming there's a mismatch in the parameter mapping.
So, first try setting the same name for the parameter and the form's input element.
Another problem is that the JSON is uploaded as a (regular) value of a text input in the form, not as a separate part in the multi-part/MIME. So there's no content-type header associated with it to find out that Spring should use the JSON deserializer.
You can use #RequestParam instead and register a specific converter like in this answer: JSON parameter in spring MVC controller
this may help you, while receiving MultipartFile you should set request header content-type to "multipart/form-data" , then in your controller use consumes="multipart/form-data" , consumes also used to map our request to our method in controller.
If you want to receive JSON data , better to send request in the form of JSONString , just receive that jsonstring, later convert into json Object format then, use that object for yours operations.
check below code :
#RequestMapping(value="/savingImg", method=RequestMethod.POST,
consumes="multipart/form-data", produces="application/json")
public ResponseEntity<?> savingAppIMgDtlss(
#RequestParam(value="f1", required = false) MultipartFile f1 ,
#RequestParam(value="f2", required = false) MultipartFile f2 ,
#RequestParam(value="f3", required = false) MultipartFile f3 ,
#RequestParam(value="f4", required = false) MultipartFile f4 ,
#RequestParam(value="f5", required = false) MultipartFile f5 ,
#RequestParam(value="f6", required = false) MultipartFile f6 ,
#RequestParam(value="f7", required = false) MultipartFile f7 ,
#RequestParam(value="f8", required = false) MultipartFile f8 ,#RequestParam("data") String jsonString)
throws Exception , ParseException {
try{
JSONObject gstcTranObj = new JSONObject();
//converting JSONString to JSON
net.sf.json.JSONObject jsonDtls = net.sf.json.JSONObject.fromObject(jsonString);
System.out.println("f1::"+f1.getOriginalFilename());
System.out.println("f2::"+f2.getOriginalFilename());
System.out.println("f3::"+f3.getOriginalFilename());
System.out.println("f4::"+f4.getOriginalFilename());
System.out.println("f5::"+f5.getOriginalFilename());
System.out.println("f6::"+f6.getOriginalFilename());
System.out.println("f7::"+f7.getOriginalFilename());
System.out.println("f8::"+f8.getOriginalFilename());
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<>("Failed",HttpStatus.NOT_FOUND);
}finally{
}
return new ResponseEntity<>("Success", HttpStatus.OK);
}
}
Exception is thrown because you don't have appropriate HttpMessageConverter, to process multipart/form-data request.
Workaround

Categories