对于文件的下载功能来说,我归纳为 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
吧。