Upload file using #FeignClient in SpringBoot - java

I have two apps, both are on SpringBoot. I'm trying to upload file from one app to another using #FeignClient
Here is Controller code that accepts file upload
#PostMapping(
path = "/result/output/{resultId}",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
#ResponseStatus(HttpStatus.OK)
public Result uploadOutputProviderMetadata(#PathVariable(value = "resultId") UUID resultId,
#RequestParam("file") MultipartFile multipartFile) {
return resultService.storeResult(providerSearchTaskId, getString(multipartFile));
}
Here is the test snippet call for this controller
new MockMultipartFile(
"file",
file.getName(),
"application/zip",
FileUtils.readFileToByteArray(file));
var resultAsString = getMockMvc()
.perform(MockMvcRequestBuilders
.multipart("/private/api/v1/result/output/" + resultId.toString(), getPort())
.file(multipartFile)
.contentType(MediaType.MULTIPART_FORM_DATA)
)
.andExpect(status().isOk())
.andReturn()
.getResponse()
.getContentAsString();
Test works, MvcMock can upload file using MockMultipartFile and MediaType.MULTIPART_FORM_DATA
Here is my poor FeignClient from another app that tries to upload file
#FeignClient(name = "Client", url = "${server.url}")
trait UploadClient {
#PostMapping(
path = Array("/private/api/v1/result/output/{resultId}"),
consumes = Array(MediaType.MULTIPART_FORM_DATA_VALUE))
def uploadResult(#PathVariable resultId: UUID,
#RequestPart(name = "file") file: MultiValueMap[String, Object]): Result
}
}
#Configuration
#EnableFeignClients(clients = Array(classOf[UploadClient]))
class FeignActivationConfiguration {
#Bean
def clientFeignEncoder(messageConverters: ObjectFactory[HttpMessageConverters]): Encoder = {
new SpringFormEncoder(new SpringEncoder(messageConverters))
}
}
I have e2e test and feign throws
status 400 reading AdsManagerClient#uploadResult(UUID,MultiValueMap)
feign.FeignException$BadRequest: status 400 reading UploadClient#uploadResult(UUID,MultiValueMap)
Why?
I've added error decoder, controller wasn't very informative
// Error response code: 400, responseText: ERROR, reason: null
val errorMessage = s"Error response code: ${response.status()}, responseText: $responseText, reason: ${response.reason()} while calling $methodKey"

Related

upload multipart file springboot mockMvc Required request part 'file' is not present, testing controller

Getting this 'file' not present error when trying to test uploading a file, controller works fine outside testing though. Is there a reason the file isn't present here?
controller
public UploadResponse uploadFile(
#RequestPart
MultipartFile file,
#RequestParam(value = “name”)
String name) {}
test for controller
MockMultipartFile file
= new MockMultipartFile(
"photo.jpeg",
"photo.jpeg",
MediaType.IMAGE_JPEG_VALUE,
"photo".getBytes()
);
this.mockMvc.perform(
multipart(“/uploadfile”)
.file(file)
.param(“name”, “bob”))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().contentType("application/json"))
}
The name of the file should be "file" here
MockMultipartFile file
= new MockMultipartFile(
"file",
"photo.jpeg",
MediaType.IMAGE_JPEG_VALUE,
"photo".getBytes()
);

Multipart file with document name not found

I am getting this error in test case for following api.
Required request part 'document' is not present
#PostMapping(value = "/testModel/{modelName}", consumes = {
MediaType.MULTIPART_FORM_DATA_VALUE })
public ResponseEntity<Object> testModel(#PathVariable("modelName") String modelName,
#RequestPart MultipartFile document) {
}
following is the test case for above api
#Test
#Transactional
void testModel() throws Exception {
service.save(domainModelDTo);
FileInputStream fi2 = new FileInputStream(new File("test.pdf"));
MockMultipartFile file = new MockMultipartFile("file", "test.pdf", DEFAULT_BINARY.toString(), fi2);
log.debug("file name {}", file.getOriginalFilename()); // test.pdf print in log
log.debug("file length {}", file.getBytes().length); // 44782 print in log
mockMvc.perform(multipart(ENTITY_API_URL, domainModelDTo.getName()).file(file)).andExpect(status().isOk());
}
I have implemented this from the following link as it is:
https://www.baeldung.com/sprint-boot-multipart-requests
In your MockMultipartFile the first parameter (name) must match the request parameter property (in this case document).
So if you change your code to:
MockMultipartFile file = new MockMultipartFile("document", "test.pdf", DEFAULT_BINARY.toString(), fi2);
The problem should be resolved.

Spring Boot MultipartFile always has value of null

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

File is always null in srpingboot 1.3.7 when processing multipart/form-data request

I am trying to communicate a grails application with a springboot application. I am trying to upload a file to the springboot application, that is previously uploaded to grails.
This is the code of my springboot controller:
#ResponseBody
#PostMapping(value = "/save", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
def save(#Valid #ModelAttribute SaveRequest request) {
//Some logic
}
The saveRequest class contains some validations that need to be applied to the request
class SaveHotelAccreditationRequest {
//Other fields
#NotNull
MultipartFile image
}
I have set the properties in the application.properties file like so
spring.http.multipart.enabled = true
spring.http.multipart.location=/home/user/temp
I am using both commons-io and commons-fileupload libraries by Apache.
I have set the multipart resolver like this
#Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(2*1024*1024);
return multipartResolver;
}
This is the code in my grails application
CredentialsProvider credsProvider = new BasicCredentialsProvider()
credsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(userName, password))
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.build()
try {
HttpPost httpPost = new HttpPost(url)
MultipartEntityBuilder reqEntity = MultipartEntityBuilder.create()
reqEntity.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
reqEntity.addBinaryBody('image', file, ContentType.MULTIPART_FORM_DATA, file.getName())
//Other fields
httpPost.setEntity(reqEntity.build())
System.out.println("Executing request " + httpPost.getRequestLine())
CloseableHttpResponse response = httpclient.execute(httpPost)
try {
System.out.println("----------------------------------------")
System.out.println(response.getStatusLine())
System.out.println(EntityUtils.toString(response.getEntity()))
} finally {
response.close()
}
} finally {
httpclient.close()
}
And when i run the request i get the following error message:
2020-05-02 02:37:24 | WARN | org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver - Resolved
exception caused by Handler execution: org.springframework.validation.BindException: org.springframework.validatio
n.BeanPropertyBindingResult: 1 errors
Field error in object 'saveHotelAccreditationRequest' on field 'image': rejected value [null]; codes [NotNull.saveRequest.image,NotNull.image,NotNull.org.springframework.web.multipart.MultipartFile,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [saveRequest.image,image]; arguments []; default message [image]]; default message [may not be null]
I have tried using postman and i get the same error message. I cannot figure out what i am missing here, any help will be appreciated.
In Spring-Boot REST for multipart APIs, I am writing my multi-part APIs like this
#PostMapping("/upload-doc")
#ApiImplicitParam(paramType = "body", name = "payload", dataType = "UploadDocRequest")
#ResponseStatus(HttpStatus.CREATED)
#ApiOperation("Upload User's documents")
public UploadDocRequest uploadDocRequest(
#RequestPart("payload") UploadDocRequest payload, #RequestPart("docs") MultipartFile[] docs) {
Spring Multipart config in yaml
spring:
servlet:
multipart:
max-file-size: 5MB
max-request-size: 5MB
And How to call it from PostMan-
While working with Multipart , the following has worked for me in Spring boot.
#PostMapping(value = "/uploadVideo")
public void uploadVideo(HttpServletRequest request, HttpServletResponse response,
#RequestParam("file") MultipartFile file) throws Exception {
// logic here
}
and the multipart resolver which you are using for max size can be configured in properties file like this
# Max file size.
spring.servlet.multipart.max-file-size=200MB
# Max Request Size
spring.servlet.multipart.max-request-size=215MB
This I am calling from Angular App like this
var reqHeader=new HttpHeaders();
reqHeader.append('contentType','multipart/form-data')
let options = { headers: reqHeader, params: new HttpParams()
                    
             };
const formData = new FormData();
formData.append('file', file )
return this.http.post(this.url_toreadFile,formData,options)
Hope it might provide some help in your implementation.

Sending List<MultipartFile> from one service to other gives "Current Request is not a multipart request" exception

Taking in a list of multipart files and sending as a post request.
Followed Uploading a List of MultipartFile with Spring 4 restTemplate (Java Client & RestController)
Sending List from one service to other gives Current Request is not a multipart request exception message
#RestController
#RequestMapping(values = ["/rest/send"])
class Controller {
#PostMapping
fun send(#RequestParam(value ="id") id: String, #RequestParam(value="files") files: List<MultipartFile): String {
val headers = HttpHeaders()
headers.contentType = MediaType.MULTIPART_FORM_DATA
val reqBody = LinkedMultiValueMap<String,Any>()
reqBody.add("id",id)
for(file in files) {
val resource: ByteArrayResource = object ByteArrayResource(file.bytes) {
override fun getFileName():String() {
return ""
}
reqBody.add("files",resource)
}
val reqEntity = HttpEntity(reqBody,headers)
val restTemplate = RestTemplate()
restTemplate.postForEntity<String>(url,reqEntity,String::class.java)
return "sent"
}
}

Categories