web-dev-qa-db-ja.com

POST RestTemplateを使用したInputStream

POSTサーバーに多数の大きなjsonファイルを必要とするクライアントがあります。各ファイルをメモリに読み込み、ファイル全体をポストすることで、それを機能させることができました。 RestTemplateを使用します。ただし、大きなjsonファイルを処理すると、クライアントでメモリがすぐに不足します。ストリーミングアプローチに切り替えたいのですが、RestTemplateでFileInputStreamを適切に使用する方法がわかりません。 this質問 と受け入れられた回答で与えられたコードを使用しましたが、それでもメモリ使用量と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);
20
Tom

しないでください。 Resource を適切な RestTemplate#exchange メソッドと組み合わせて使用​​します。

HttpEntityResourceとしてbodyを作成します。クラスパスリソースを表す ClassPathResource があります。 RestTemplateは、デフォルトで、ストリーミングするResourceHttpMessageConverterを登録します。

11

@ sotirios-delimanolisの回答に加えて、RestTemplateにこの設定を指定して、内部でorg.springframework.http.HttpOutputMessageorg.springframework.http.StreamingHttpOutputMessageとして認識されるようにする必要もあります。内部ストリームなので、メモリにロードするだけです。このようにして、元のストリームのチャンクを使用して送信します。

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

StreamingHttpOutputMessageの実装は1つしかなく、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));
1
Sam