Search code examples
javaspringspring-bootmicroservicesnetflix-eureka

Exception on sending multipart file request to eureka rest service end point


I am developing a file repository to store the pdf files. I am using eureka + spring boot which is in producer and consumer architecture. The client is having

public boolean uploadBooks(MultipartFile file, String fileLocation, String fileName) {
    boolean callResponse = false;
    try {
        if(!file.isEmpty()) {
            ByteArrayResource fileResource = new ByteArrayResource(file.getBytes()) {
                public String getFileName() {
                    return file.getOriginalFilename();
                }
            };
        LinkedMultiValueMap<String, Object> requestMap = new LinkedMultiValueMap<>();
        requestMap.add("file", fileResource);
        requestMap.add("repoLocation", fileLocation);
        requestMap.add("fileId", fileName);

        HttpHeaders requestHeader = new HttpHeaders();
        requestHeader.setContentType(MediaType.MULTIPART_FORM_DATA);
        HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<LinkedMultiValueMap<String, Object>>(
                requestMap, requestHeader);
        System.err.println(requestEntity.getBody());
        System.err.println(uploadBooks);
        ResponseEntity<Boolean> response = restTemplate.exchange(uploadBooks,HttpMethod.POST, requestEntity,
                Boolean.class);
        callResponse = response.getBody();
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    return callResponse;
}

and the rest end point is having

@PostMapping("/uploadFile")

public boolean uploadFile(@RequestParam("file") MultipartFile file,
        @RequestParam("repoLocation") String repoLocation, @RequestParam("fileId") String fileId) {

    boolean process = false;
    try {
    System.out.println("---->"+fileId);
    process = fileServices.saveFile(file, repoLocation, fileId);
    } catch (RepoStorageException e) {

        e.printStackTrace();
    }
    if (process) {
        return true;
    }
    return false;

}

when I am uploading file I am getting

org.springframework.web.client.HttpClientErrorException$BadRequest: 400 null

exception. Please help me on writing the end point to handle the client request.

Error Stack trace

org.springframework.web.client.HttpClientErrorException$BadRequest: 400 null
at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:79)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:97)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:79)
at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:777)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:735)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:669)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:578)
at com.library.remoteservices.repository.LibraryFileRepository.uploadBooks(LibraryFileRepository.java:76)
at com.library.LibraryServiceDiscoveryClient.FileServiceController.uploadBooks(FileServiceController.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:215)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:142)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:998)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:901)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:875)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:770)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)

Solution

  • Now-a-days there are projects like Spring Content provide a very similar programming model for unstructured data (i.e. files, images, videos, etc) as Spring Data does for structured data. Plus it allows you to associate those files with your Entities.

    So (assuming you are using Spring Boot) you would add the following dependencies:

    pom.xml

       <!-- Java API -->
       <dependency>
          <groupId>com.github.paulcwarren</groupId>
          <artifactId>spring-content-fs-boot-starter</artifactId>
          <version>0.4.0</version>
       </dependency>
       <!-- REST API -->
       <dependency>
          <groupId>com.github.paulcwarren</groupId>
          <artifactId>spring-content-rest-boot-starter</artifactId>
          <version>0.4.0</version>
       </dependency>
    

    Add the following attributes to your File entity so that content can be associated with it.

    File.java

    @Entity
    public class File {
    
       ...existing fields...
    
       @ContentId
       private UUID contentId;
    
       @ContentLength
       private long contentLength = 0L;
    
       // if you have rest endpoints
       @MimeType
       private String mimeType;
    
       ...
    }
    

    Create a ContentStore (the equivalent of your FileRepository but for your files):

    FileContentStore.java

    @StoreRestResource(path="fileContents")
    public interface FileContentStore extends ContentStore<File, UUID> {
    }
    

    When you run your application Spring Content will see the FileContentStore interface and the spring-content-fs dependency and inject a Filesystem implementation of this interface for you. It will also see the spring-content-rest dependency and add an @Controller implementation that forwards GET, PUT, POST and DELETE REST requests onto FileContentStore as well. This saves you from having to write any of the controller code you have above. REST endpoints will be available at /fileContents so...

    curl -X POST -F "image=@/path/to/local/file.pdf" /fileContents/{fileId}

    will upload file.pdf and associate it with your file entity. And:

    curl /fileContents/{fileId} will fetch it again.

    This programming model also creates an abstraction over the file storage implementation allowing you to choose any type of storage that Spring Content supports (currently, this is Filesystem, S3, JPA BLOBs, Mongo's Gridfs and Google Storage). Your application code will stay the same regardless.

    HTH