So I'm making a RESTful API backend for a JavaScript frontend and want to upload a file to Google Cloud Storage. I have this function to handle the file upload:
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST, consumes = { "multipart/form-data" })
public ResponseEntity<?> uploadFile(#ModelAttribute("file") FileDTO fileDTO) {
FunctionResponse uploadResponse = cloudStorage.uploadObject(fileDTO.getFile());
if (uploadResponse.successful()) {
return new ResponseEntity<>(uploadResponse.getMessage(), HttpStatus.OK);
} else {
return new ResponseEntity<>(uploadResponse.getMessage(), HttpStatus.BAD_REQUEST);
}
}
My fileDTO class looks like this:
public class FileDTO implements Serializable {
private MultipartFile file;
public MultipartFile getFile() {
return file;
}
}
However whenever I try and access the MultipartFile it always throws a java.lang.NullPointerException.
I have tried various SO answers from threads with similar problems but none have worked so far.
I have multipart.enabled=true in my application.properties.
Any help would be appreciated and let me know if you need any more information.
When a multipart/form-data request arrives, the data must be obtained by using #RequestParam, not #ModelAttribute + if the value you need of the request is a file, then, it should be deserialized into a MultipartFile object.
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST, consumes = {"multipart/form-data"})
public ResponseEntity<?> uploadFile(#RequestParam(name = "file") MultipartFile file) {
// your handle ...
}
This is my controller:
#PostMapping(value = "/endpoint", produces = { APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE})
#ResponseBody
public Result generateResult(#Valid #RequestBody Request request) throws JsonProcessingException {
Result result = new Result();
// some code here
return result;
}
and this is my Request class:
#Data
#NoArgsConstructor
public class Request {
#NotNull
private String name;
private String type = "application/json";
}
the controller produces the correct output based on the Accept header in the request sent by the client. However, I want to send no Accept header and only send the following request:
{
"name": "my name",
"type": "application/xml"
}
Then based on the type the correct format should be output. I tried to add HttpServletResponse response to the parameter list of the controller method and then set the content type like this:
response.setHeader(CONTENT_TYPE, request.geType());
but it always returns json. any idea what else I should do?
I think a standard Spring's ResponseEntity builder give you all needed variety:
return ResponseEntity
.ok(//any object for json structure)
.headers(//any header)
.build();
Instead .ok() you can you any other method (that's http status code)
or
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("MyHeader", "MyValue");
return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.OK);
based on the comments I post this answer which worked for me. I changed my controller method like this:
#PostMapping(value = "/endpoint", produces = { APPLICATION_JSON_VALUE,
APPLICATION_XML_VALUE})
#ResponseBody
public ResponseEntity<Result> generateResult(#Valid #RequestBody Request request)
throws JsonProcessingException {
Result result = new Result();
// some code here
return ResponseEntity.accepted()
.headers(headers)
.body(result);
}
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
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.
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