RestTemplate 下载文件

2,173 阅读2分钟

「这是我参与11月更文挑战的第31天,活动详情查看:2021最后一次更文挑战

1. RestTemplate

RestTemplate 作为 SpringBoot 中推荐使用的 HTTP 请求工具,提供了众多的功能和可自定义化的配置。

除了进行 HTTP 请求获取请求数据外,RestTemplate 还可以用来上传和下载网络文件。

使用 RestTemplate 工具上传文件可以参考文章:使用RestTemplate上传文件,而本篇文章就来介绍一下如何使用 RestTemplate 工具实现文件下载。

2. 文件下载服务

想要使用 HTTP 请求下载文件,首先需要提供下载文件的服务,通过 SpringBoot 定义一个下载文件的服务。

  • 文件下载服务接口定义
@RequestMapping(method = RequestMethod.GET, value = "/getFile")
public HttpEntity<InputStreamResource> getFile() throws IOException {
    //读取本地文件,将文件流封装为 InputStream 对象
    File file = new File("D:\test.txt");
    InputStream inputStream = new FileInputStream(file);
    InputStreamResource inputStreamResource = new InputStreamResource(inputStream);

    // 文件作为响应体 body 数据,和响应头一起创建 HttpEntity,最终返回
    MultiValueMap<String, String> headers = new HttpHeaders();
    // 设置下载的文件名
    headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=test.txt");
    HttpEntity<InputStreamResource> httpEntity = new HttpEntity<>(inputStreamResource, headers);
    return httpEntity;
}

3. 文件下载请求

使用 RestTemplate 进行 HTTP 请求下载文件

3.1 byte[] 接收文件内容

  • RestTemplate 请求获取文件时,对于小文件可以使用 byte[] 类型接收,此种情况会依次读取整个文件到内存中,文件过大时可能会导致内存不够用,发生 OOM 异常

  • GET 方法获取文件

public static byte[] getFile(){
    ResponseEntity<byte[]> responseEntity = restTemplate.getForEntity("http://127.0.0.1:8099/getFile", byte[].class);
    return responseEntity.getBody();
}
  • POST 请求、表单传参下载文件字节数组
public static byte[] downFileByFormData(String url, Map headers, MultiValueMap<String,String> body){
    headers.put("Content-Type","application/x-www-form-urlencoded");
    HttpHeaders header = new HttpHeaders();
    header.setAll(headers);
    HttpEntity<Object> httpEntity = new HttpEntity(body,header);

    ResponseEntity<byte[]> response = restTemplate.postForEntity(url,httpEntity,byte[].class);
    
    if(response.getStatusCode().value() != HttpStatus.OK.value()){
        log.info("请求失败!");
        return new byte[]{};
    }
    return response.getBody();
}

3.2 流式读取

对于较大大文件,可以使用 streamInput 流式读取,边读边操作,避免内存溢出的问题。

使用 RestTemplate 的 execute() 方法,在参数中匿名类实现 ResponseExtractor 接口并实现其中方法,或者使用 Lambda 表达式。

  • GET 请求,无传参获取文件
public static void downLargeFileByStream(String url, String savePath){
    // 对响应进行流式处理而不是将其全部加载到内存中
    restTemplate.execute(url, HttpMethod.GET, null, response -> {
        Files.copy(response.getBody(), Paths.get(savePath));
        return null;
    }, httpEntity);
}
  • POST 请求,传递请求头,传入表单参数
public static void downLargeFileByStream(String url, Map headers, MultiValueMap<String,String> body, String savePath){
    headers.put("Content-Type","application/x-www-form-urlencoded");
    HttpHeaders header = new HttpHeaders();
    header.setAll(headers);
    HttpEntity<Object> httpEntity = new HttpEntity(body,header);

    //定义请求头的接收类型,无请求信息时可以设置为 null
    RequestCallback requestCallback = restTemplate.httpEntityCallback(httpEntity, null);

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

    // 对响应进行流式处理而不是将其全部加载到内存中
    restTemplate.execute(url,HttpMethod.POST,requestCallback, response -> {
        Files.copy(response.getBody(), Paths.get(savePath));
        return null;
    }, httpEntity);
}
  • 获取文本字符串并写入到文本文件中
// 大文件时可以使用流式边读边操作,
String result = restTemplate.execute("http://127.0.0.1:8099/getFile", HttpMethod.GET, null, new ResponseExtractor<String>() {
    @Override
    public String extractData(ClientHttpResponse response) throws IOException {
        System.out.println("状态:"+response.getStatusCode());
        System.out.println("头:"+response.getHeaders());

        //获取响应体流
        InputStream body = response.getBody();

        //处理响应体流
        String content = body.toString();
        return content;
    }
});

//将请求到的数据写入到文件中
File file = new File("/test.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(responseEntity.getBody());
fileOutputStream.close();