Spring Boot MultipartFile always has value of null - java

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 ...
}

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

error : The server refused this request because the request entity is in a format not supported by the requested resource for the requested method

I am making a simple API using Spring. and i am getting this error while uploading and mapping file.
The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.
this is my Controller ->
#RequestMapping(value = "/Hi", method = RequestMethod.POST, produces = { "application/json" })
#ResponseBody
public BasicResponse UploadData(#RequestBody CropImageData cropImageData, HttpServletRequest request) {
BasicResponse basicResponse = new BasicResponse();
System.out.println(cropImageData.getCropId());
return basicResponse;
}
My cropImageData model class ->
public class CropImageData {
#JsonProperty("cropImages")
private MultipartFile[] cropImages;
#JsonProperty("cropId")
private String cropId;
public MultipartFile[] getCropImages() {
return cropImages;
}
public void setCropImages(MultipartFile[] cropImages) {
this.cropImages = cropImages;
}
public String getCropId() {
return cropId;
}
public void setCropId(String cropId) {
this.cropId = cropId;
}
}
this is how i am sending request via POSTMAN.
With Postman, you are sending a HTTP post form-data but your end point is not configured to receive this format (the consumes = { "multipart/form-data" } annotation is missing).
Instead of your model class, you should change the signature of your method to something like that:
public BasicResponse UploadData(#RequestPart("cropId") String cropId, #RequestPart("cropImages") MultipartFile file)

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.

Conditional statements in Spring Controller questions

So a have a blueprint code of a web app which allows different accounts with logins to upload simple files. However, there are security flaws that allow non-admin accounts to enter database configurations and direct object referencing (download files of other accounts by URL tampering). I was recommended a solution whereby I check if the owner of the uploaded file is the same one trying to download it. But doing that in a spring #controller posed a few problems. If you look at my get statement to get a specific file, you see that I got the file object and the account object. Then I just check if the name is the same of file owner. But how do I "return" something in a controller while in an "if" statement?
#Controller
public class FileController {
#Autowired
private FileRepository fileRepository;
#Autowired
private AccountRepository accountRepository;
#RequestMapping(value = "/files", method = RequestMethod.GET)
public String list(Authentication authentication, Model model) {
model.addAttribute("files", accountRepository.findByUsername(authentication.getName()).getFileObjects());
return "files";
}
#RequestMapping(value = "/files/{id}", method = RequestMethod.GET)
public ResponseEntity<byte[]> viewFile(#PathVariable Long id) {
//1. get object or name account name
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentPrincipalName = authentication.getName();
//2. check if the file account is of the same name
FileObject fo = fileRepository.findOne(id);
if((fo.getAccount().getUsername()).equals(currentPrincipalName)) {
System.out.println("WHAT AM I SUPPOSED TO DO!?");
}
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(fo.getContentType()));
headers.add("Content-Disposition", "attachment; filename=" + fo.getName());
headers.setContentLength(fo.getContentLength());
return new ResponseEntity<>(fo.getContent(), headers, HttpStatus.CREATED);
}
#RequestMapping(value = "/files", method = RequestMethod.POST)
public String addFile(Authentication authentication, #RequestParam("file") MultipartFile file) throws IOException {
Account account = accountRepository.findByUsername(authentication.getName());
FileObject fileObject = new FileObject();
fileObject.setContentType(file.getContentType());
fileObject.setContent(file.getBytes());
fileObject.setName(file.getOriginalFilename());
fileObject.setContentLength(file.getSize());
fileObject.setAccount(account);
fileRepository.save(fileObject);
return "redirect:/files";
}
#RequestMapping(value = "/files/{id}", method = RequestMethod.DELETE)
public String delete(#PathVariable Long id) {
fileRepository.delete(id);
return "redirect:/files";
}
}

How to get JSON as Response in Spring MVC?

I am trying to return response as JSON. After searching I found solution to add headers = "Accept=application/json" in RequestMapping. But still it is not working .
It is throwing error HTTP Status 406 "The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers."
Here is my controller code :
#RestController
public class EmployeeController {
private EmployeeService employeeService;
#Autowired(required = true)
#Qualifier(value = "employeeService")
public void setEmployeeService(EmployeeService employeeService){
this.employeeService = employeeService;
}
#RequestMapping(value = "/test",method = RequestMethod.GET)
public String test(){
return "{\"name\":\"xyz\"}";
}
#RequestMapping(value = "/employees",method = RequestMethod.GET,headers = "Accept=application/json")
public List<Employee> listEmployees(){
List<Employee> employees = this.employeeService.getEmployees();
return employees;
}
}
Where am I doing wrong?
The simple way to generate JSON, XML response is #ResponseBody annotation.
#RequestMapping(value =" /jsonPostSingle", method = RequestMethod.GET)
#ResponseBody
public PostModel generateJSONPostsingle(#ModelAttribute("postModel") PostModel postModel) {
if(postModel.getPostId() == 1) {
postModel.setTitle("post title for id 1");
} else {
postModel.setTitle("default post title");
}
return postModel;
}
This way you will be able to map your request to model class using #ModelAttribute.
Follow the complete tutorial Spring MVC : JSON response using #ResponseBody
I understand that you're trying to send a response from GET request of /employees.
if you are using Spring 3.1, try to use
#RequestMapping(value = "/employees",method = RequestMethod.GET, produces = "application/json")
instead of adding headers = "Accept=application/json"
More info:
if you want to specify type of data that will send with a request, you can use consumes attribute
example:
#RequestMapping(value="/foo", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
this will consumes and response with JSON type only
check this link about spring update http://spring.io/blog/2011/06/13/spring-3-1-m2-spring-mvc-enhancements/
Hope it helps

Categories