[Java]Spring/SpringBoot提供文件下载功能,以及解决下载中文乱码问题

2021-03-01 1989点热度 0人点赞 0条评论

前言

介绍如何在Spring/SpringBoot中实现文件下载,以及解决输出文件名是中文名称时,遇到的中文乱码问题。

实现

如果你用的Spring而不是SpringBoot,需要额外配置下MessageConverters

  • 代码配置:新建(如果没有的话)一个@Configuration的Class(extends WebMvcConfigurerAdapter)实现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);
}

或者直接写到HttpServletResponseOutputStream里,示例如下:

@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>

admin

这个人很懒,什么都没留下

文章评论

您需要 登录 之后才可以评论