前言
介绍如何在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>
文章评论