MultipartFile Restemplate 전송시 오류

2020, May 19    

파일을 api로 전송하는 로직을 구현 중에 발생한 오류에 대한 기록

  • 구현중인 로직은 MultipartFile 리스트를 Spring RestTemplate을 이용하여 전송하는 로직이었다.

  • 코드는 다음과 같다.

  private ResUploadFileVO postMultiPartRequest(String url, ReqFileUploadFileVO param) throws Exception {

    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.set("Content-Type", MediaType.MULTIPART_FORM_DATA_VALUE);

    //param, header set
    MultiValueMap<String, Object> body = getMultiPartParam(param);
    ResponseEntity<String> result
            = apiRestTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(body, httpHeaders), String.class);

    return this.responseResultProcess(result, ResUploadFileVO.class);
}

private MultiValueMap<String, Object> getMultiPartParam(ReqFileUploadFileVO param) throws IOException {

    MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
    body.add("svcCd", param.getSvcCd());
    body.add("savePath", param.getSavePath());
    body.add("upFile", param.getUpFile());
    body.add("upFiles", param.getUpFiles());
    return body;
}
    


  • 오류 메시지
      Type definition error: [simple type, class java.io.FileDescriptor]; 
      nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 
      No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: 
      org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile["inputStream"]->java.io.FileInputStream["fd"])] with root cause
    



    에러 메시지를 보니, multipartFile 형태의 필드를 convert하지 못하는 것으로 보이는데, 통 감이 잡히지 않았다.

    구글링을 해보니 RestTemplate의 기본 MessageConverters가 MultipartFile 파일에 포함 된 InputStream을 직렬화하는 방법을 알지 못하기 때문에 예외가 발생하는 것이라고 한다.
    해결을 위해서는 request 전송시 MultipartFile 자체 대신 MultipartMap에 MultipartFile의 바이트를 추가 하여 해결 가능하다.

  • 참고링크

  • 위 링크를 보고 적용 했더니 잘된다. 한가지 알아 둘것이, 파일리스트를 전달하고자 할시 MultipartMap에 리스트 자체를 set해주면 안되고 add해줘야 한다.

  • 변경 코드
      private MultiValueMap<String, Object> getMultiPartParam(ReqFileUploadFileVO param) throws IOException {
          MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
          body.add("svcCd", param.getSvcCd() == null ? this.svcCode : param.getSvcCd());
          body.add("savePath", param.getSavePath());
    
          if (param.getUpFile() != null) {
              body.add("upFile",
                      new MultipartInputStreamFileResource(param.getUpFile().getInputStream(), param.getUpFile().getOriginalFilename()));
          }
    
          if (param.getUpFiles() != null) {
              for (MultipartFile file : param.getUpFiles()) {
                  body.add("upFiles", new MultipartInputStreamFileResource(file.getInputStream(), file.getOriginalFilename()));
              }
          }
    
          return body;
      }
    
      class MultipartInputStreamFileResource extends InputStreamResource {
    
          private final String filename;
    
          MultipartInputStreamFileResource(InputStream inputStream, String filename) {
              super(inputStream);
              this.filename = filename;
          }
    
          @Override
          public String getFilename() {
              return this.filename;
          }
    
          @Override
          public long contentLength() throws IOException {
              return -1; // we do not want to generally read the whole stream into memory ...
          }
      }