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