RestTemplate 下载文件
前言
大家平时工作中用的较多可能是使用 RestTemplate 调用 api,api 返回结果一般是 json,RestTemplate 将 json 反序列化为对象。不过今天我们这里要分享的是使用 RestTemplate 下载文件,包括下载大文件。
使用 RestTemplate 下载文件
RestTemplate 提供了 execute 方法,利用该方法我们可以读取响应体,将响应体存放到本地文件中,execute 方法签名如下:
public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException
这里需要比较重要的是 responseExtractor 参数,它的类型为 ResponseExtractor:
@FunctionalInterface
public interface ResponseExtractor<T> {
@Nullable
T extractData(ClientHttpResponse response) throws IOException;
}
ResponseExtractor 是一个函数式接口,这使得我们可以通过 lambda 表达式的形式(要求Java8)拿到 response,之后便是读取 response,并将读取到的内容存储到文件中。举个例子:
File file = restTemplate.execute("http://download/urlpath", HttpMethod.GET, null, response -> {
File ret = File.createTempFile("download", "tmp");
try (FileOutputStream out = new FileOutputStream(ret)) {
StreamUtils.copy(response.getBody(), out);
return ret;
}
});
以上介绍了一种使用 RestTemplate 下载文件的方式,但需要说明的是,以上方式不仅仅适用于下载文件,事实上响应体本质是一系列 bytes,我们只是将这些 bytes 存放到了文件中。
RestTemplate 下载大文件
当文件很大时,如果由于网络或者其他原因导致下载中断,这个时候最好能从中断的地方再次下载,否则从头开始下载一个大文件是很让人沮丧的。当然这需要服务端能够支持部分请求,单靠 RestTemplate 是无能为力的。
通过 head 请求我们可以测试某个下载地址是否支持部分请求(partial request)。服务器端一般通过 Accept-Ranges 响应头告诉客户端它支持部分请求。
HttpHeaders headers = restTemplate.headForHeaders("http://your/download/path");
如果 headers 中包含 Accept-Ranges 响应头,那么服务端支持部分请求。这样便可以通过 RestTemplate 来实现部分请求:
File file = new File("/path/to/save/file");
restTemplate.execute(
"http://your/download/path",
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;
});
这里有几个问题需要讲解:
Q1: contentLength 从哪里来?
之前我们为了测试服务端是否支持部分请求,发送了一个 head 请求,从这个请求的响应中一般可以读取到 contentLength,使用 headers.get("Content-Length)
即可。如果读取不到怎么办?
好办!将 String.format("bytes=%d-%d", file.length(), contentLength)
改成 String.format("bytes=%d-", file.length())
。
"Range" 请求头就是为了告诉服务端我们需要哪个区间的文件内容,例如:
- bytes=100- 表示我们需要下载从 100 字节开始的内容直到文件末尾。
- bytes=100-1000 表示我们需要下载从 100 到 1000 字节区间范围的文件内容。
Q2: new FileOutputStream(file, true)
第二个参数 true 的作用?
true 表示追加,由于是部分请求,我们可能一开始请求到了 1000 字节处,由于某种原因下载中断了,此时我们可以从 1001 字节处开始再次下载,如果没有 true,再次下载的内容会覆盖之前下载的内容,我们需要的是将本次下载的内容追加到上次已经下载的内容后面,最终完成整个大文件的下载。
温馨提示:反馈需要登录