복붙노트

[SPRING] restTemplate을 사용하여 Multipart 폼 데이터를 보내는 방법 Spring-mvc

SPRING

restTemplate을 사용하여 Multipart 폼 데이터를 보내는 방법 Spring-mvc

Jetty와 함께 RestTemplate이 포함 된 파일을 Raspberry Pi에 업로드하려고합니다. 파이에는 서블릿이 실행 중이다.

protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

    PrintWriter outp = resp.getWriter();

    StringBuffer buff = new StringBuffer();

    File file1 = (File) req.getAttribute("userfile1");
    String p = req.getParameter("path");
    boolean success = false;

    if (file1 == null || !file1.exists()) {
        buff.append("File does not exist\n");
    } else if (file1.isDirectory()) {
        buff.append("File is a directory\n");
    } else {
        File outputFile = new File(req.getParameter("userfile1"));
        if(isValidPath(p)){
            p = DRIVE_ROOT + p;
            final File finalDest = new File(p
                    + outputFile.getName());
            success = false;
            try {
                copyFileUsingFileChannels(file1, finalDest);
                finalDest.setWritable(true);
                success = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (success){
                buff.append("File successfully uploaded.\n");
            }
            else{
                                    buff.append("Failed to save file.");
            }
        }
        else{
            buff.append("Invalid path.\n");
        }
    }
    outp.write(buff.toString());
}

나는 성공적으로 컬로 할 수있다.

curl --form userfile1=@/home/pi/src/CreateNewFolderServlet.java --form press = OK localhost : 2222 / pi / GetFileServlet? path = "/ media /"

이것은 webapp에서 동일한 기능을 사용하기로되어있는 메소드입니다.

@ResponseBody 
@RequestMapping(value="/upload/",method=RequestMethod.POST ,produces = "text/plain")
public String uploadFile(MultipartHttpServletRequest request2, HttpServletResponse response2){

    Iterator<String> itr =  request2.getFileNames();

     MultipartFile file = request2.getFile(itr.next());
     System.out.println(file.getOriginalFilename() +" uploaded!");

    System.out.println(file.toString()); 
     MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
    parts.add("userfile1",file);
    //reqEntity.addPart("userfile1", file);
    String path="/public/";
    RestTemplate restTemplate = new RestTemplate();
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
    System.out.println("1");
    HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<MultiValueMap<String, Object>>(parts, headers);
    String url =  url2+"/pi/GetFileServlet?path="+path;
    System.out.println("2");
/*  restTemplate.getMessageConverters().add(new FormHttpMessageConverter());
    restTemplate.getMessageConverters().add(
            new MappingJackson2HttpMessageConverter());*/
    System.out.println("3");
    ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, request,String.class);
    System.out.println("4");
    System.out.println("response : " +response);
    if(response==null||response.getBody().trim()==""){
        return "error";
    }
    return response.getBody();
}

이것은 내가 얻는 결과입니다 :

ui-elements.html이 (가) 업로드되었습니다.

org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@47e7673e

1

2

3

당신이 볼 수 있듯이 숫자 4는 인쇄되지 않습니다. 콘솔에서는 예외가 없습니다. 디버깅 중에 발견 된 예외 :

org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: No serializer found for class java.io.ByteArrayInputStream and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: org.springframework.web.multipart.support.StandardMultipartFile["inputStream"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class java.io.ByteArrayInputStream and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: org.springframework.web.multipart.support.StandardMultipartFile["inputStream"])

해결법

  1. ==============================

    1.RestTemplate의 기본 MessageConverters가 MultipartFile 파일에 포함 된 InputStream을 직렬화하는 방법을 알지 못하기 때문에 예외가 발생합니다. RestTemplate을 통해 객체를 보낼 때 대부분의 경우 POJO를 보내려고합니다. MultipartFile 자체 대신 MultipartMap에 MultipartFile의 바이트를 추가하여이 문제를 해결할 수 있습니다.

    RestTemplate의 기본 MessageConverters가 MultipartFile 파일에 포함 된 InputStream을 직렬화하는 방법을 알지 못하기 때문에 예외가 발생합니다. RestTemplate을 통해 객체를 보낼 때 대부분의 경우 POJO를 보내려고합니다. MultipartFile 자체 대신 MultipartMap에 MultipartFile의 바이트를 추가하여이 문제를 해결할 수 있습니다.

    서블릿 부분에도 문제가 있다고 생각합니다. 예를 들어

    File file1 = (File) req.getAttribute("userfile1");
    

    ServletRequest의 getAttribute 메소드는 요청 / 양식 매개 변수가 아니라 서블릿 컨텍스트에 의해 설정된 속성을 리턴하므로 항상 null을 리턴해야합니다. 실제로 컬 예를 사용하고 있습니까?

    다음은 서블릿에 파일을 전달하는 Spring MVC 메소드의 예이다.

    서블릿 (Spring MVC 컨테이너에서 실행되는 것을 테스트 했음에도) :

    @RequestMapping("/pi")
    private void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    
      final String path = request.getParameter("destination");
      final Part filePart = request.getPart("file");
      final String fileName = request.getParameter("filename");
    
      OutputStream out = null;
      InputStream fileContent = null;
      final PrintWriter writer = response.getWriter();
    
      try {
        out = new FileOutputStream(new File(path + File.separator
                + fileName));
        fileContent = filePart.getInputStream();
    
        int read = 0;
        final byte[] bytes = new byte[1024];
    
        while ((read = fileContent.read(bytes)) != -1) {
          out.write(bytes, 0, read);
        }
        writer.println("New file " + fileName + " created at " + path);
    
      } catch (FileNotFoundException fne) {
        writer.println("You either did not specify a file to upload or are "
                + "trying to upload a file to a protected or nonexistent "
                + "location.");
        writer.println("<br/> ERROR: " + fne.getMessage());
    
      } finally {
        if (out != null) {
          out.close();
        }
        if (fileContent != null) {
          fileContent.close();
        }
        if (writer != null) {
          writer.close();
        }
      }
    }
    

    스프링 MVC 메소드 :

    @ResponseBody
    @RequestMapping(value="/upload/", method=RequestMethod.POST, 
            produces = "text/plain")
    public String uploadFile(MultipartHttpServletRequest request) 
            throws IOException {
    
      Iterator<String> itr = request.getFileNames();
    
      MultipartFile file = request.getFile(itr.next());
      MultiValueMap<String, Object> parts = 
              new LinkedMultiValueMap<String, Object>();
      parts.add("file", new ByteArrayResource(file.getBytes()));
      parts.add("filename", file.getOriginalFilename());
    
      RestTemplate restTemplate = new RestTemplate();
      HttpHeaders headers = new HttpHeaders();
      headers.setContentType(MediaType.MULTIPART_FORM_DATA);
    
      HttpEntity<MultiValueMap<String, Object>> requestEntity =
              new HttpEntity<MultiValueMap<String, Object>>(parts, headers);
    
      // file upload path on destination server
      parts.add("destination", "./");
    
      ResponseEntity<String> response =
              restTemplate.exchange("http://localhost:8080/pi", 
                      HttpMethod.POST, requestEntity, String.class);
    
      if (response != null && !response.getBody().trim().equals("")) {
        return response.getBody();
      }
    
      return "error";
    }
    

    이들을 사용하여 다음과 같은 컬에 의해 MVC 메소드를 통해 서블릿에 파일을 성공적으로 업로드 할 수 있습니다.

    curl --form file=@test.dat localhost:8080/upload/
    
  2. ==============================

    2.ByteArrayResource에서 전체 파일을 읽는 것은 대용량 파일의 메모리 소비 문제 일 수 있습니다.

    ByteArrayResource에서 전체 파일을 읽는 것은 대용량 파일의 메모리 소비 문제 일 수 있습니다.

    InputStreamResource를 사용하여 spring mvc 컨트롤러에서 파일 업로드를 프록시 처리 할 수 ​​있습니다.

    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    public ResponseEntity<?> uploadImages(@RequestPart("images") final MultipartFile[] files) throws IOException {
        LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        String response;
        HttpStatus httpStatus = HttpStatus.CREATED;
    
        try {
            for (MultipartFile file : files) {
                if (!file.isEmpty()) {
                    map.add("images", new MultipartInputStreamFileResource(file.getInputStream(), file.getOriginalFilename()));
                }
            }
    
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.MULTIPART_FORM_DATA);
    
            String url = "http://example.com/upload";
    
            HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, headers);
            response = restTemplate.postForObject(url, requestEntity, String.class);
    
        } catch (HttpStatusCodeException e) {
            httpStatus = HttpStatus.valueOf(e.getStatusCode().value());
            response = e.getResponseBodyAsString();
        } catch (Exception e) {
            httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
            response = e.getMessage();
        }
    
        return new ResponseEntity<>(response, httpStatus);
    }
    
    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 ...
        }
    }
    
  3. from https://stackoverflow.com/questions/28408271/how-to-send-multipart-form-data-with-resttemplate-spring-mvc by cc-by-sa and MIT license