I use Jhipster and this is a controller method:
Controller:
#RequestMapping(value = UPLOAD_URL, method = {RequestMethod.POST},
headers = {"content-type=multipart/mixed", "content-type=multipart/form-data"},
consumes = {"multipart/form-data"})
public ResponseEntity<?> uploadWithMetaData(#RequestPart(value = "file") MultipartFile file,
#RequestPart(value = "documentDTO") DocumentDTO documentDTO,
Locale locale) throws IOException, URISyntaxException, JSONException {
// business logic
}
Essentially I want to post a file and also a json object.
I my integration test, I can verify that it works as expected:
Integration test:
DocumentDTO documentDTO = getDocumentDTOMockFile();
Long originId = originRepository.findAll().stream().findFirst().get().getId();
documentDTO.setOriginId(originId);
MockMultipartFile jsonFile = new MockMultipartFile("documentDTO", "", "application/json",
jsonUtils.toJson(documentDTO, null).getBytes());
restClientMockMvc
.perform(MockMvcRequestBuilders.fileUpload("/api/v1/documents/upload")
.file(fstmp)
.file(jsonFile))
.andDo(MockMvcResultHandlers.log())
.andExpect(status().isOk());
}
Angular frontend:
let fd: FormData = new FormData();
let file = fileForm.files[0];
fd.append("file", file);
let documentDTO = JSON.stringify(document);
fd.append("documentDTO",new Blob([JSON.stringify({
"documentDTO": documentDTO})], {
type: "application/json"
})
);
his.httpClient.post("/api/v1/documents/upload", fd ).subscribe(request => {
console.log("request", request);
});
I got an interceptor that sets the content-type in the request headers to:
Content-Type:multipart/form-data; boundary=----WebKitFormBoundary4PnIOSOLe5Djj95R
This is how the Request payload looks like:
This is the spring boot log message:
Resolved exception caused by Handler execution: org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present
This is the response I see in the browser:
{
"type" : "http://www.jhipster.tech/problem/problem-with-message",
"title" : "Bad Request",
"status" : 400,
"detail" : "Required request part 'file' is not present",
"path" : "///api/v1/documents/upload",
"message" : "error.http.400"
}
What I've tried:
setting content-type to 'Content-Type' : 'multipart/mixed' => result same
Creating a pojo with the dto and the file, using #ModelAttribute => same error
Then I checked if I got a Multipart Resolver, got it
I'm out of ideas, someone any suggestions?
Post as a multipart-form from the JavaScript and use something like this:
final WebRequest webRequest,
#RequestParam("fileContent") final MultipartFile fileContent,
#RequestParam("inputJson") String inputJsonString
as the parameters.
The WebRequest is useful if you need to access the session.
Related
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
I'm new to MockMVC. I've successfully written some basic tests, but I got stuck on trying to test an use case with the endpoint that requires a POST request with two parameters - a POJO and an array of MultipartFile. The test is written as such:
#Test
public void vytvorPodnetTest() throws Exception {
var somePojo = new SomePojo();
somePojo.setSomeVariable("test_value");
var roles = List.of("TEST_USER");
var uid = "00000000-0000-0000-0000-000000000001";
MockMultipartFile[] attachments = {new MockMultipartFile("file1.txt", "file1.txt", "text/plain", "file1 content".getBytes()),
new MockMultipartFile("file2.txt", "file2.txt", "text/plain", "file2 content".getBytes())};
MockMultipartHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart("/some-pojo/create");
builder.with(req - {
req.setMethod("POST");
return req;
});
MvcResult result = mockMvc.perform(builder.file(attachments[0]).file(attachments[1])
.param("SomePojo", new ObjectMapper().writeValueAsString(somePojo))
.file(attachment[0])
.with(TestUtils.generateJWTToken(uid, roles)))
.andExpect(status.isOk())
.andReturn();
}
The controller method is as follows:
#PostMapping(value = "/create", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public UUID createPojo(
#RequestPart(value = "SomePojo") SomePojo somePojo,
#RequestPart(value = "attachments", required = false) MultipartFile[] attachments) {
return pojoService.create(somePojo, attachments);
}
It stops here, before reaching the service. I've tried adding the files both as a param "attachments" and like shown above, but all I get is "400 Bad Request"
Finally found the way to send the parameters as MockMultipartFile from MockMVC to the controller:
MockMultipartFile pojoJson = new MockMultipartFile("SomePojo", null,
"application/json", JsonUtils.toJSON(podnet).getBytes());
mockMvc.perform(MockMvcRequestBuilders.multipart("/some-pojo/create")
.file(pojoJson)
.contentType(MediaType.MULTIPART_FORM_DATA_VALUE)
.with(new TestUtils().generateJWTToken(uid, roles)))
.andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
I want to upload the file with spring boot and vue.
But, I have an error '415 : Unsupported MediaType'.
This is my spring boot controller.
#PostMapping(consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE})
#ResponseBody
#ApiOperation(value = "회원 가입", notes = "<strong>아이디와 패스워드</strong>를 통해 회원가입 한다.")
#ApiResponses({
#ApiResponse(code = 200, message = "성공"),
#ApiResponse(code = 401, message = "인증 실패"),
#ApiResponse(code = 404, message = "사용자 없음"),
#ApiResponse(code = 500, message = "서버 오류")
})
public ResponseEntity<User> register(
#RequestParam("file") MultipartFile file,) throws IllegalStateException, IOException, URISyntaxException {
...
}
And, this is my vue code.
<el-form-item prop="profileImg" label="프로필 사진" :label-width="state.formLabelWidth" >
<input #change="fileSelect()" id="profileimg" type="file" accept="image/*">
</el-form-item>
const formData = new FormData()
formData.append('userId', state.form.userId)
formData.append('userName', state.form.userName)
formData.append('password', state.form.password)
formData.append('age', state.form.age)
formData.append('gender', state.form.gender)
formData.append('phoneNum', state.form.phoneNum)
formData.append('email', state.form.email)
formData.append('mbti', state.form.mbti)
formData.append('guide', state.form.guide)
formData.append('file', state.form.profileImg)
profileImg ({ commit }, payload) {
return axios
.post(`${BASE_URL}/api/v1/users`, payload,
{
headers: {
"Accept": "*/*",
"Content-Type": "multipart/form-data"
}
}
)
}
I tried #RequestPart MultipartFile file, but had the same error.
How can I fix it?
Sorry, I can't add comments, this answer is just my guess
what if you output
request.getHeader(HttpHeaders.CONTENT_TYPE);
Content-Type may have been changed if your request pass through Nginx or other agent
I found an answer here on GitHub
All you have to do is change
#RequestParam("file") MultipartFile file)
with :
#RequestParam("file") FilePart file)
I want to test my Spring Controller that has input json and multipart file, however even if the function works, I can't get the test works
I tried using MockMvcRequestBuilders.fileUpload but the test always get 405
The controller:
#PutMapping(value = "/upload", consumes = {"multipart/form-data"})
public BaseResponse<SomeModel> updateSomeModelContent(
#ApiIgnore #Valid #ModelAttribute MandatoryRequest mandatoryRequest,
#PathVariable("id") String id,
#RequestParam("file") MultipartFile file,
#RequestParam("json") String json) throws IOException {
final CommonSomeModelRequest request = JSONHelper.convertJsonInStringToObject(json, CommonSomeModelRequest.class);
return makeResponse(someModelSer.updateContent(id, request, mandatoryRequest, file));
}
The test:
#Test
public void updateCountryContentSuccessTest() throws Exception {
MockMultipartFile file1 = new MockMultipartFile("file", "filename-1.jpeg", "image/jpeg", "some-image".getBytes());
MockMultipartFile file2 = new MockMultipartFile("json", "", "application/json","{\"exampleAttr\": \"someValue\"}".getBytes());
when(this.someModelService
.updateContent(id, request, MANDATORY_REQUEST, file1))
.thenReturn(someModelUpdatedContent);
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders
.fileUpload("/upload/{id}", id)
.file(file1)
.file(file2)
.requestAttr("mandatory", MANDATORY_REQUEST);
this.mockMvc.perform(builder)
.andExpect(status().isOk());
verify(this.someModelService)
.updateContent(id, request, MANDATORY_REQUEST, file1);
}
The result status is always 405, I don't know how to make it 200
MockMvcRequestBuilders.multipart(...) create MockMultipartHttpServletRequestBuilder which support only POST.
But in your controller you use POST. You should change PUT to POST mapping in the rest controller.
Also according to RFCs we should use POST instead of PUT on multipart upload.
Have a look at Spring MVC Framework: MultipartResolver with PUT method
I'm trying to send the following request through my Controller
#RequestMapping(value = {"/fileupload"}, method = RequestMethod.POST, consumes = {MediaType.ALL_VALUE)
#ResponseStatus(value = HttpStatus.OK)
#ResponseBody
public Object fileupload(#RequestPart MultipartFile[] file,#RequestPart DTO dto) throws Exception {
}
But I get the following error
CORSFilter HTTP Request: POST
org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:226)
at org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver.resolveArgument(RequestPartMethodArgumentResolver.java:132)
I face the same issue and i resolved this on Angular side using below code. On the server side it is fine, you can even remove consume.ALL
url: 'api/import_bot',
data: {
file: vm.file,
DTO: new Blob([angular.toJson(DTO)], {type : 'application/json'})
}
You have to send file and wrap the DTO as BLOB and set its content-type.