【问题标题】:POST InputStream with RestTemplate使用 RestTemplate 发布 InputStream
【发布时间】:2016-03-07 00:58:49
【问题描述】:

我有一个客户端需要将大量大型 json 文件发布到服务器。我已经能够通过将每个文件读入内存并使用 RestTemplate 发布整个文件来使其工作。但是,客户端在处理大型 json 文件时会很快耗尽内存。我想切换到流式处理方法,但不知道如何正确使用带有 RestTemplate 的 FileInputStream。我找到了this question 并使用了接受的答案中给出的代码,但我仍然看到内存使用情况和 OutOfMemory 异常,这让我相信它不是流式传输文件,而是仍然将它们完全读入内存。我究竟做错了什么?这是我目前拥有的:

final InputStream fis = ApplicationStore.class.getResourceAsStream(path);

final RequestCallback requestCallback = new RequestCallback() {
    @Override
    public void doWithRequest(final ClientHttpRequest request) throws IOException {
        request.getHeaders().add("Content-type", "application/json");
        IOUtils.copy(fis, request.getBody());
    }
};

final RestTemplate restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);     
restTemplate.setRequestFactory(requestFactory);     
final HttpMessageConverterExtractor<String> responseExtractor =
         new HttpMessageConverterExtractor<String>(String.class, restTemplate.getMessageConverters());

restTemplate.execute("http://" + host + ":8080/upads-data-fabric" + "/ruleset", httpMethod, requestCallback, responseExtractor);

【问题讨论】:

    标签: java resttemplate


    【解决方案1】:

    不要。将Resource 与适当的RestTemplate#exchange 方法结合使用。

    使用Resource 作为body 创建一个HttpEntityClassPathResource 代表类路径资源。 RestTemplate 默认注册一个ResourceHttpMessageConverter

    在内部,ResourceHttpMessageConverter 将请求内容流式传输到与 StreamUtils#copy(InputStream, OutputStream) 的连接的另一端,buffer size 当前设置为 4096。

    【讨论】:

      【解决方案2】:

      除了@sotirios-delimanolis 答案之外,您还需要为您的RestTemplate 指定此设置,以便在内部将您的org.springframework.http.HttpOutputMessage 识别为org.springframework.http.StreamingHttpOutputMessage,否则它只会将整个流复制到其内部流,所以您只需将其加载到内存中。这样,它会使用原始流的块并发送它们。

      HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
      requestFactory.setBufferRequestBody(false);
      restTemplate.setRequestFactory(requestFactory);
      

      我这么说是因为StreamingHttpOutputMessage 只有一个实现,而HttpComponentsClientHttpRequestFactory 是唯一创建它的地方。

      可重现的例子:

      MultiValueMap<String, Object> bodyMap = new LinkedMultiValueMap<>();
      UrlResource urlResource = new UrlResource(MY_EXTERNAL_FILE_URL) { //uses URL#inputStream
          @Override
          public String getFilename() {
              return FILE_NAME;
          }
      };
      bodyMap.add("file", urlResource); //other service uses -- @RequestParam("file") MultipartFile -- in its controller
      RequestEntity<MultiValueMap<String, Object>> request =
          RequestEntity.post(URI.create("http://localhost:6666/api/file"))
              .contentType(MediaType.MULTIPART_FORM_DATA)
              .body(bodyMap);
      
      //should be a @Bean
      RestTemplate restTemplate = new RestTemplate ();
      HttpComponentsClientHttpRequestFactory requestFactory = new 
      HttpComponentsClientHttpRequestFactory();
      requestFactory.setBufferRequestBody(false);
      restTemplate.setRequestFactory(requestFactory);
      
      System.out.println(restTemplate.exchange(request, FileMetadata.class));
      

      【讨论】:

      猜你喜欢
      • 2016-07-22
      • 1970-01-01
      • 1970-01-01
      • 2020-11-17
      • 2017-10-07
      • 2013-04-30
      • 2017-10-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多