前言
介绍如何在Spring/SpringBoot中实现文件下载,以及解决输出文件名是中文名称时,遇到的中文乱码问题。
实现
如果你用的Spring而不是SpringBoot,需要额外配置下MessageConverters:
- 代码配置:新建(如果没有的话)一个
@Configuration的Class(extendsWebMvcConfigurerAdapter)实现configureMessageConverters() ,增加converters.add(new ResourceHttpMessageConverter());@Configuration public class MyWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new StringHttpMessageConverter()); converters.add(new ResourceHttpMessageConverter()); // 增加这个 converters.add(new BufferedImageHttpMessageConverter()); // 输出BufferedImage作为图片使用 super.configureMessageConverters(converters); } } - XML配置(spring-servelt.xml):
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter" /> <!-- 主要增加ResourceHttpMessageConverter --> <bean class="org.springframework.http.converter.ResourceHttpMessageConverter" /> <bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/> </mvc:message-converters> </mvc:annotation-driven> <!-- 其他的省略了 --> </beans> 
如果是SpringBoot就不需要更多额外的配置了,下面直接写Controller里的方法:
@RequestMapping(value = "/download/{name}.{ext}", method = RequestMethod.GET)
public ResponseEntity<ByteArrayResource> downloadFile(
        @PathVariable("name") String name, @PathVariable("ext") String ext) throws Exception {
    String fileName = name + "." + ext;
    File baseDir = new File("xxx"); // 文件存放路径省略
    File file = new File(baseDir, fileName);
    if (!file.exists()) { // 文件不存在
        throw new HttpClientErrorException(HttpStatus.NOT_FOUND);
    }
    Path path = Paths.get(file.getAbsolutePath());
    ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(path));
    //解决输出中文乱码问题(如果fileName有中文的话)
    String percentEncodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8)
            .replaceAll("\\+", "%20");
    HttpHeaders headers = new HttpHeaders();
    String contentDispositionValue = "attachment; filename=" +
            percentEncodedFileName + "; filename*=utf-8''" + percentEncodedFileName;
    headers.add(HttpHeaders.CONTENT_DISPOSITION, contentDispositionValue);
    return ResponseEntity.ok()
            .headers(headers)
            .contentLength(file.length())
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .body(resource);
}
或者直接写到HttpServletResponse的OutputStream里,示例如下:
@RequestMapping(value = "/download/{filename}.{ext}", method = RequestMethod.GET)
public void downloadFile(@PathVariable("filename") String filename, @PathVariable("ext") String ext, HttpServletResponse response) throws IOException {
    File baseDir = new File("xxx"); // 文件存放路径省略
    String fileName = filename + "." + ext;
    // 解决中文文件乱码问题
    String percentEncodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8)
            .replaceAll("\\+", "%20");
    String contentDispositionValue = "attachment; filename=" +
            percentEncodedFileName + "; filename*=utf-8''" + percentEncodedFileName;
    response.setHeader("Content-Disposition", contentDispositionValue);
    response.setContentType("application/octet-stream");
    File file = new File(baseDir, fileName);
    if (file.exists()) {
        throw new HttpClientErrorException(HttpStatus.NOT_FOUND);
    }
    // 将文件流写到response中
    // 1 第一种做法,利用try with resource和apache commons io (maven引入见下一段)
    // try (InputStream inputStream = new FileInputStream(file); OutputStream outputStream = response.getOutputStream()) {
    //    IOUtils.copy(inputStream, outputStream);
    // }
    // 将文件流写到response中
    // 2 第二种经典做法,把File的InputStream 读出到respononse的OutputStream
    OutputStream outputStream = response.getOutputStream();
    InputStream inputStream = new FileInputStream(file);
    byte[] buf = new byte[4096];
    while (true) {
        int n = inputStream.read(buf);
        if (n <= 0)
            break;
        outputStream.write(buf, 0, n);
        outputStream.flush();
    }
    outputStream.close();
}
apache commons.io maven引入:
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-io</artifactId>
    <version>3.9</version>
</dependency>
文章评论