1. 개요
이 예제에서, 우리는 방법에 대한 서로 다른 기술을 보여줄거야 큰 파일을 다운로드 와 RestTemplate을 .
2. RestTemplate
RestTemplate 은 Spring 3에서 도입 된 차단 및 동기 HTTP 클라이언트입니다. Spring 문서 에 따르면버전 5에서 반응 형 비 차단 HTTP 클라이언트로 WebClient 를도입했기 때문에 향후 사용되지 않을 예정입니다.
3. 함정
일반적으로 파일을 다운로드 할 때 파일 시스템에 저장하거나 바이트 배열로 메모리에로드합니다. 그러나 대용량 파일 인 경우 메모리 내로드로 인해 OutOfMemoryError 가 발생할 수 있습니다 . 따라서 응답 청크를 읽을 때 파일에 데이터를 저장해야합니다.
먼저 작동하지 않는 몇 가지 방법을 살펴 보겠습니다.
먼저 Resource 를 반환 유형으로 반환하면 어떻게 되나요 ?
Resource download() {
return new ClassPathResource(locationForLargeFile);
}
이것이 작동하지 않는 이유는 ResourceHttpMesssageConverter 가 전체 Response body을 ByteArrayInputStream에 로드하여 여전히 피하고 싶은 메모리 압력을 추가하기 때문입니다.
둘째, InputStreamResource를 반환하고 ResourceHttpMessageConverter # supportsReadStreaming을 구성 하면 어떻게됩니까? 음, 이것은 InputStreamResource.getInputStream ()을 호출 할 때까지 " 소켓 폐쇄" 오류가 발생하기 때문에 작동하지 않습니다 ! 이는 “execute ”가 종료 전에 응답 입력 스트림을 닫기 때문 입니다.
그렇다면 문제를 해결하기 위해 무엇을 할 수 있습니까? 실제로 여기에도 두 가지가 있습니다.
- 반환 형식으로 File 을 지원 하는 사용자 지정 HttpMessageConverter 작성
- 사용자 지정 ResponseExtractor 와 함께 RestTemplate.execute 를 사용 하여 입력 스트림을 파일 에 저장합니다.
이 예제에서는 두 번째 솔루션이 더 유연하고 더 적은 노력이 필요하기 때문에 사용합니다.
4. 이력서없이 다운로드
임시 파일에 본문을 작성 하는 ResponseExtractor 를 구현해 보겠습니다 .
File file = restTemplate.execute(FILE_URL, HttpMethod.GET, null, clientHttpResponse -> {
File ret = File.createTempFile("download", "tmp");
StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(ret));
return ret;
});
Assert.assertNotNull(file);
Assertions
.assertThat(file.length())
.isEqualTo(contentLength);
여기서는 StreamUtils.copy 를 사용하여 FileOutputStream 의 응답 입력 스트림을 복사 했지만 다른 기술과 라이브러리 도 사용할 수 있습니다.
5. 일시 중지 및 재개로 다운로드
대용량 파일을 다운로드 할 예정이므로 어떤 이유로 든 일시 중지 한 후에 다운로드를 고려하는 것이 좋습니다.
먼저 다운로드 URL이 이력서를 지원하는지 확인하겠습니다.
HttpHeaders headers = restTemplate.headForHeaders(FILE_URL);
Assertions
.assertThat(headers.get("Accept-Ranges"))
.contains("bytes");
Assertions
.assertThat(headers.getContentLength())
.isGreaterThan(0);
그런 다음 RequestCallback 을 구현하여 "Range"헤더를 설정하고 다운로드를 재개 할 수 있습니다.
restTemplate.execute(
FILE_URL,
HttpMethod.GET,
clientHttpRequest -> clientHttpRequest.getHeaders().set(
"Range",
String.format("bytes=%d-%d", file.length(), contentLength)),
clientHttpResponse -> {
StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(file, true));
return file;
});
Assertions
.assertThat(file.length())
.isLessThanOrEqualTo(contentLength);
정확한 콘텐츠 길이를 모르는 경우 String.format을 사용하여 Range 헤더 값을 설정할 수 있습니다 .
String.format("bytes=%d-", file.length())
6. 결론
큰 파일을 다운로드 할 때 발생할 수있는 문제에 대해 논의했습니다. 또한 RestTemplate 을 사용하는 동안 솔루션을 제시했습니다 . 마지막으로 재개 가능한 다운로드를 구현하는 방법을 보여주었습니다.
- https://docs.spring.io/spring-framework/docs/current/reference/html
- https://www.baeldung.com/spring-resttemplate-download-large-file
'Java' 카테고리의 다른 글
정수의 제곱근이 Java에서 정수인지 확인 (0) | 2021.03.22 |
---|---|
RestTemplate을 사용하여 개체 List 가져 오기 및 게시 (0) | 2021.03.22 |
Junit 테스트 중에 ApplicationRunner 또는 CommandLineRunner Bean이 실행되지 않도록 방지 (0) | 2021.03.21 |
WebRTC 사용방법(예제) (1) | 2021.03.21 |
자바 컬렉션 (0) | 2021.03.21 |