Java 如何使用SpringRESTTemplate恢复下载?

Java 如何使用SpringRESTTemplate恢复下载?,java,spring,spring-boot,resttemplate,Java,Spring,Spring Boot,Resttemplate,我正在使用RestTemplate从Nexus服务器(大约350MB)下载一个文件。post中提供的代码在这方面效果良好: RestTemplate restTemplate // = ...; // Optional Accept header RequestCallback requestCallback = request -> request.getHeaders() .setAccept(Arrays.asList(MediaType.APPLICATION_O

我正在使用RestTemplate从Nexus服务器(大约350MB)下载一个文件。post中提供的代码在这方面效果良好:

RestTemplate restTemplate // = ...;

// Optional Accept header
RequestCallback requestCallback = request -> request.getHeaders()
        .setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));

// Streams the response instead of loading it all in memory
ResponseExtractor<Void> responseExtractor = response -> {
    // Here I write the response to a file but do what you like
    Path path = Paths.get("some/path");
    Files.copy(response.getBody(), path);
    return null;
};
restTemplate.execute(URI.create("www.something.com"), HttpMethod.GET, requestCallback, responseExtractor);
但这只会导致OOM错误:

java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source) ~[na:1.8.0_191]
    at java.io.ByteArrayOutputStream.grow(Unknown Source) ~[na:1.8.0_191]
    at java.io.ByteArrayOutputStream.ensureCapacity(Unknown Source) ~[na:1.8.0_191]
    at java.io.ByteArrayOutputStream.write(Unknown Source) ~[na:1.8.0_191]
    ...
我已经使用带有curl的Range头测试了这个调用,并且确信Nexus支持这个功能。我是这样设置的:

long bytes = path.toFile().length();
...
request.getHeaders().setRange(Arrays.asList(HttpRange.createByteRange(bytes)));
我猜上面的内存错误是因为InputStream阻塞。因此,我尝试改用通道/缓冲区:

...
try {
    if(Files.exists(path)) {
        log.info("{} exists. Attempting to resume download", path);
        ReadableByteChannel channel = Channels.newChannel(response.getBody());

        FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.APPEND);
        fileChannel.tryLock();
        ByteBuffer buffer = ByteBuffer.allocate(4096);
        int bytesRead = 0;
        while((bytesRead = channel.read(buffer)) != -1) {
            fileChannel.write(buffer);
            buffer.clear();
        }

        fileChannel.close();    
    } else {
        Files.copy(response.getBody(), path);
    }
...
这至少会将一些数据写入文件,但仍然失败。我在java.nio工具方面没有太多经验,因此非常感谢您的帮助


*编辑:非常感谢您的回答,但我不得不在这个项目中使用JDK 8。

如果您正在处理大文件,则需要以下代码行,以确保不会在内存中读取流:

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

谢谢Alexandar,我一直在使用HttpComponents客户端HttpRequestFactory,但同样的逻辑也适用。@lucidMonkey那么它能工作吗?将缓冲设置为false。这是一个必要的添加。上面使用filechannel和buffer的代码的另一个关键问题是缺少行“buffer.flip()”,这是必需的。有了这两个变化,它似乎运转良好。
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);     
restTemplate.setRequestFactory(requestFactory);