Unable to post multipart data and requestbody form data json request - java

Im trying to post data from postman. I have an method receiving RequestBody and Multipart file. But im steel having this error. The way i use to store images, is working in other implementations when i save only one image.
{
"timestamp": "2020-02-06T19:52:12.566+0000",
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'multipart/form-data;boundary=--------------------------833603313116090653834108;charset=UTF-8' not supported",
}
#PostMapping(value = "new", headers=("content-type=multipart/*"), consumes = "multipart/form-data" )
private Product save(#RequestBody Product product, #RequestParam("files") MultipartFile[] files){
var disk = new Disk("product");
Product productSaved = new Product();
String fileName;
try {
if (files != null && files.length >0) {
productSaved = service.save(product);
for (MultipartFile file : files) {
fileName = disk.saveImage(file);
Images image = new Images(fileName, productSaved);
imagesService.saveImage(image);
}
} else {
return null;
}
} catch (IOException e) {
e.printStackTrace();
}
return productSaved;
}

The first thing is the MultipartFile cannot be inside the body of the Response from your client. The second thing is the data response from your browser should only be only one way.
My suggestion is you can put all the data you want to save to the database inside a form submission. And use #ModelAttribute Product product, #RequestParam MultipartFile[] files .
Also, your method can be void since you save data, you don't need to return anything.
Note: #ModelAttribute can be omitted.

You can take both params as RequestParam and covert json body to Object using objectMapper like below

Related

Error uploading JSON data and Image in single API call: Spring Boot

I am working on a Spring Boot Project where I want to send JSON data and Multipart File (Image) in a single API call. For this, I referred- https://blogs.perficient.com/2020/07/27/requestbody-and-multipart-on-spring-boot/#:~:text=Usually%20we%20add%20%40RequestBody%20and,So%2C%20annotation%20should%20be%20altered.
My Controller is-
#PostMapping(value = "/create",consumes = {MediaType.APPLICATION_JSON_VALUE,MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<SuccessResponse<PostDto>> createPost(
#RequestPart("post") String post,
#RequestPart("image") MultipartFile file,
#RequestParam(name = "userid") Integer uid,
#RequestParam(name = "categoryid") Integer categoryId) {
log.info("Filename :" + file.getOriginalFilename());
log.info("Size:" + file.getSize());
log.info("Contenttype:" + file.getContentType());
//convert the post string to POJO
PostDto postDto=postService.getJson(post);
//Now create the post
PostDto newPost = this.postService.createPost(postDto, uid, categoryId, file);
SuccessResponse<PostDto> successResponse = new SuccessResponse<>(AppConstants.SUCCESS_CODE,
AppConstants.SUCCESS_MESSAGE, newPost);
return new ResponseEntity<>(successResponse, HttpStatus.OK);
}
I am using Postman for testing-
When I make the request, I get the following error (Note- I have set the error response in Spring Security as seen in the image.)
[nio-8085-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required part 'image' is not present.]
I tried another approach but it gives another error-
#PostMapping("/uploadimage/{postid}/{isUpdatingPost}")
public ResponseEntity<SuccessResponse<String>> uploadImage(#RequestParam(name="file") MultipartFile file, #PathVariable("postid") int postid, #PathVariable("isUpdatingPost")boolean isUpdatingPost){
String result=this.postService.uploadImage(file, postid, isUpdatingPost);
SuccessResponse<String> response=new SuccessResponse<>(AppConstants.SUCCESS_CODE,AppConstants.SUCCESS_MESSAGE,result);
return new ResponseEntity<>(response,HttpStatus.OK);
}
[Request processing failed: org.springframework.web.multipart.MultipartException: Current request is not a multipart request] with root cause
org.springframework.web.multipart.MultipartException: Current request is not a multipart request
I am not able to understand the problem in these approaches. I also set the content type to multipart/form-data in Postman and the consume parameter in #PostMapping, but still getting these errors.
Please help in finding the problem!
This worked for me.
For attaching single file with #RequestParam:
#PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> uploadImage(#RequestParam MultipartFile file) throws IOException {
// ...
}
For attaching file in DTO with #ModelAttribute:
public record FileDTO(
Integer id,
MultipartFile file) {
}
#PostMapping(value = "/2", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.IMAGE_JPEG_VALUE)
public ResponseEntity<?> uploadImage2(#ModelAttribute FileDTO dto) throws IOException {
// ...
}
BTW, you probably want use OpenAPI(Swagger UI) for manually testing your app, it's easier than using postman.
Reference article: https://www.baeldung.com/spring-file-upload

Receiving image as stream through Java Spring Boot via REST POST

I'm creating a project in Java with Spring Boot.
The focus is to receive an image that is converted to a stream and that my code converts this image to a pdf file and sends this pdf back as a stream.
Despite the analysis, I can't get past the beginning, receiving the stream.. .
Here you'll see a snippet of my postman call to the running project
My Controller looks like this:
#RestController
public class Controller {
#PostMapping(value = "/convert/{format}", consumes = "application/octet-stream", produces = "application/octet-stream")
#ResponseBody
public void convert(RequestEntity<InputStream> entity, HttpServletResponse response, #PathVariable String format, #RequestParam Map<String, String> params) throws IOException {
if ("pdf".equalsIgnoreCase(format)) {
PDFConverter cnv = new PDFConverter();
/*cnv.convert(entity.getBody(), response.getOutputStream(), params);*/
response.setContentType("application/octet-stream");
response.getOutputStream().println("hello binary");
} else {
// handle other formats
throw new IllegalArgumentException("illegal format: " + format);
}
}
}
What do I overlook in this case?
I found the solution, in the controller I used RequestEntity<InputStream> entity, this gave the error. After changing this to HttpServletRequest request it worked.
#RestController
public class Controller {
#RequestMapping(value="/convert/{format}", method=RequestMethod.POST)
public #ResponseBody void convert(HttpServletRequest request, HttpServletResponse response, #PathVariable String format, #RequestParam Map<String, String> params) {
try{
if ("pdf".equalsIgnoreCase(format)) {
PDFConverter cnv = new PDFConverter();
response.setContentType("application/pdf");
cnv.convert(request.getInputStream(), response.getOutputStream(), params);
} else {
// handle other formats
throw new IllegalArgumentException("illegal format: " + format);
}
} catch (IllegalArgumentException | IOException e) {
e.printStackTrace();
}
}
}
As the error message tells you already, your content-type is not valid. You expecting a different content Type than you are sending off. Might be the problem that you append the charset definition to the request.
I think you are using commons-fileupload's streaming API. This won't work if spring.http.multipart.enabled=true, due to the request being pre-processed. Can you try setting spring.http.multipart.enabled=false and also change consumes = { MediaType.MULTIPART_FORM_DATA_VALUE },

Multiple Multipart in rest call with different key

I am trying to call POST service multipart form-data with #RequestPart in SpringBoot
below is code which is working fine with multiple #RequestPart
#PostMapping(value = "/saveDetails", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void saveDetails(#RequestPart("profileImage") MultipartFile profileImage,#RequestPart("addressImage") MultipartFile addressImage, #RequestPart("requestData") String requestData) {
try {
if(CommonUtils.isObjectNullOrEmpty(requestData) || CommonUtils.isObjectNullOrEmpty(profileImage) || CommonUtils.isObjectNullOrEmpty(addressImage)){
logger.warn("Data Should not be null ==>");
}
detailSaveService.saveOrUpdateDetails(profileImage, addressImage,requestData);
} catch (Exception e) {
logger.error("Error while saving profile Details ==>", e);
}
}
I need to improve my code #RequestPart needs to be a single parameter for upload
multiple files with a different key (profileImage and addressImage).
Is there any suggestions? Please help.
I have a solution for you please check #ModelAttribute in Spring-boot please check below example
Create class and add data members for all different key parameters with getter setter please refer below example
class FileUploadRequest {
private MultipartFile profileImage;
private MultipartFile addressImage;
private MultipartFile[] images; // you can use list or array
private String requestData; // you can use another pojo or Jsonobject
// add getter setter here...
}
use #ModelAttribute in your rest controller with consume your HTTP post request with MULTIPART_FORM_DATA_VALUE
#PostMapping(value = "/saveDetails", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void saveDetails(#ModelAttribute FileUploadRequest uploadRequest) {
try {
if(CommonUtils.isObjectNullOrEmpty(uploadRequest.getRequestData()) || CommonUtils.isObjectNullOrEmpty(uploadRequest.getProfileImage()) || CommonUtils.isObjectNullOrEmpty(uploadRequest.getAddressImage())){
logger.warn("Data Should not be null ==>");
} else {
detailSaveService.saveOrUpdateDetails(uploadRequest.getProfileImage()), uploadRequest.getAddressImage()),uploadRequest.getRequestData()));
}
} catch (Exception e) {
logger.error("Error while saving profile Details ==>", e);
}
}
you can refer to this link example of #ModelAttribute with spring-boot with angular
Hope it's work for you

Required request part 'file' is not present. Trying upload an image, angular -> spring

I have my endpoint in spring-boot API for uploading images. It's working fine with the postman, but if I want to send a request from my angular app I'm getting code 400 "Required request part 'file' is not present".
I think the problem is in my angular code.
My endpoint
#PostMapping(value = "/private/image/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public BasicImgurResponse uploadProfilePicture(#RequestParam(name = "file") MultipartFile file){
try {
return service.uploadProfilePicture(file);
} catch (IOException ex) {
Logger.getLogger(ImgurController.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
}
My angular service method
uploadAvatar(data: any){
let file = new FormData();
file.append('file',data);
return this.http.post<any>(environment.api_endpoint+'/private/image/avatar',file);
}
May be data object is empty? Make sure that data object contains a input file.
Without Angular, the command will be as below:
formData.append('file', $('input[type=file]')[0].files[0]);

Trouble downloading binary file with angular $http or jquery.ajax

My problem is that I am getting the wrong sized file on the client side. Here is my #Controller ...
#RequestMapping(value = "/download/{id}", method = RequestMethod.GET)
public ResponseEntity<?> download(final HttpServletRequest request,
final HttpServletResponse response,
#PathVariable("id") final int id) throws IOException {
try {
// Pseudo-code for retrieving file from ID.
Path zippath = getZipFile(id);
if (!Files.exists(zippath)) {
throw new IOException("File not found.");
}
ResponseEntity<InputStreamResource> result;
return ResponseEntity.ok()
.contentLength(Files.size(zippath))
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(new InputStreamResource(new FileInputStream(zippath.toFile())));
} catch (Exception ex) {
// ErrorInfo is another class, unimportant
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ErrorInfo(ex));
}
}
... and here is my client-side code using angular-file-saver ...
$http({url: "export/download/" + exportitem.exportId, withCredentials: true})
.then(function(response) {
function str2bytes(str) {
var bytes = new Uint8Array(str.length);
for (var i=0; i<str.length; i++) {
bytes[i] = str.charCodeAt(i);
}
return bytes;
}
var blob = new Blob([str2bytes(response.data)], {type: 'application/octet-stream'});
FileSaver.saveAs(blob, "download.zip");
}, $exceptionHandler);
The original file is 935673 bytes but response.data is 900728 and passing it through the transformation to Uint8Array results in a Blob that is 900728 in size as well. Either way, the resulting saved file is 900728 bytes (34945 bytes shy). Also it is not quite the same in what gets written. It seems to slightly get bloated but then the last part just seems to be truncated. Any ideas what I might be doing wrong?
UPDATE
I just updated my controller method to be the following and got the exact same result. Grrr.
#RequestMapping(value = "/download/{id}", method = RequestMethod.GET)
public void download(final HttpServletRequest request,
final HttpServletResponse response,
#PathVariable("id") final int id) throws IOException {
// Pseudo-code for retrieving file from ID.
Path zippath = getZipFile(id);
if (!Files.exists(zippath)) {
throw new IOException("File not found.");
}
response.setContentType("application/zip");
response.setHeader("Content-Disposition",
"attachment; filename=download.zip");
InputStream inputStream = new FileInputStream(zippath.toFile());
org.apache.commons.io.IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
inputStream.close();
}
So the problem turned out to be angular's $http service. I also tried jQuery's ajax method. Both gave the same result. If I instead use the native XMLHttpRequest it works correctly. So the Java code was sound. I first verified this by exposing the file directly to the internet and then both using curl and directly accessing in the browser I managed to download the file of the correct size. Then I found this solution so that I could also download the file via javascript.
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "blob";
xhr.withCredentials = true;
xhr.onreadystatechange = function (){
if (xhr.readyState === 4) {
var blob = xhr.response;
FileSaver.saveAs(blob, filename);
}
};
xhr.send();
Why does angular or jQuery give the wrong result? I still don't know but if anyone wishes to give an answer that uses those it would be appreciated.
responseType: blob
did the trick for a zip file
Angular 2 +
this.http.get('http://localhost:8080/export', { responseType: ResponseContentType.Blob })
.subscribe((res: any) => {
const blob = new Blob([res._body], { type: 'application/zip' });
saveAs(blob, "fileName.zip");
i just stumbled over the 'responseType' in $http requests, you are probably looking for 'blob': https://docs.angularjs.org/api/ng/service/$http#usage

Categories

Resources