对于文件的下载功能来说,我归纳为 2
头 1
流。极其简单。
Content-Type
头,告诉客户端文件类型。Content-Disposition
头,告诉客户端对于这个文件的处理方式(在浏览器中显示,还是下载)。Output
流,写入文件内容到客户端。
文件名称乱码的问题 {#文件名称乱码的问题}
Content-Disposition
头有三个属性值,可以指定文件的名称。
name
filename
filename*
至于区别,我也说不大清楚。反正是有个规范,我感觉很乱。具体可以参考如下链接:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition
这就是容易出问题的地方了,由于文件名称设置方法不正确。或者是浏览器不兼容问题,如果 文件名称包含中文 ,可能会导致 下载文件名称乱码:
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + "中文の文件.txt");
如上,直接用 +
拼接中文字符串,在谷歌浏览器中下载就是乱码。
常见的解决方案 {#常见的解决方案}
以下我是在网上看到的一些解决方案。
-
对文件名称进行URI编码
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLEncoder.encode("中文の文件.txt", StandardCharsets.UTF_8));
-
修改字符编码
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + new String("中文の文件.txt".getBytes("GBK"),"ISO-8859-1"));
经过测试,上面2种办法都OK。
比较优雅的方式 {#比较优雅的方式}
Spring 提供了一个工具类: org.springframework.http.ContentDisposition
,专门用于处理文件下载中的 Content-Disposition
Header,它会自动处理好中文文件名称的编码问题。
ContentDisposition
使用实在是太简单了,这里就不多说了,给一个完整的使用示例就能看明白。
package io.springboot.demo.web.controller;
import java.io.IOException; import java.nio.charset.StandardCharsets;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.ContentDisposition; import org.springframework.http.HttpHeaders; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping("/demo") public class DemoController {
@GetMapping public void demo (HttpServletResponse response) throws IOException {
byte[] content = "Hello Spring".getBytes(StandardCharsets.UTF_8);
// 文件类型 response.setContentType("text/plain"); // 文本类型文件的编码 response.setCharacterEncoding(StandardCharsets.UTF_8.displayName()); // 文件长度 response.setContentLength(content.length); // 文件的处理方式。attachment 表示附件,filename 表示文件的名称 response.setHeader(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition .attachment() // 附件形式 .filename("中文の文件.txt", StandardCharsets.UTF_8) // 文件名称 & 编码 .build() .toString()); response.getOutputStream().write(content);
} }
以后文件下载接口,就不用自己去拼接、转码文件名称了,交给 ContentDisposition
吧。