规范化的响应头Content-Disposition
参考文章 https://blog.robotshell.org/2012/deal-with-http-header-encoding-for-file-download/
Content-Disposition: attachment;
filename="$encoded_fname";
filename*=utf-8''$encoded_fname
filename 参数:是标准的文件名传递方式,用于指定下载的文件名。它只支持 ASCII 字符,因此如果文件名包含非 ASCII 字符,需要对其进行编码。可以使用 URLEncoder.encode() 方法对文件名进行编码,并在 Content-Disposition 头字段中使用该编码后的文件名,其中 $encoded_fname
是对文件名进行编码后的值。
filename* 参数:这是文件名的扩展方式,使用了 RFC 5987 中的规范,其中 $encoded_fname
是对文件名进行 UTF-8 编码后的值。
2010年 RFC 5987 发布,正式规定了 HTTP Header 中多语言编码的处理方式采用parameter*=charset'lang'value
的格式,其中:
charset 和 lang 不区分大小写。
lang 是用来标注字段的语言,以供读屏软件朗诵或根据语言特性进行特殊渲染,可以留空。
value 根据 RFC 3986 Section 2.1 使用百分号编码,并且规定浏览器至少应该支持 ASCII 和 UTF-8 。
当 parameter 和 parameter* 同时出现在 HTTP 头中时,浏览器应当使用后者。
java后端配置Content-Disposition并解决中文文件名乱码问题,经测试ie或谷歌都能正常返回中文字符
String realFileName = URLEncoder.encode(realFileName, StandardCharsets.UTF_8.toString());
response.setHeader("Content-Disposition", "attachment; filename=\"" + realFileName + "\"; filename*=utf-8''" + realFileName);
疑问:filename*=utf-8''$encoded_fname
不配置可以吗,我看filename="$encoded_fname"
兼容性挺好的?
filename="$encoded_fname"
的配置通常已经足够满足大多数情况,因为它是 Content-Disposition 头字段的标准方式来指定下载的文件名。大多数现代浏览器都能正确地处理这种情况,并且它具有良好的兼容性。
虽然filename*=utf-8''$encoded_fname
提供了更好的支持,特别是对于包含非 ASCII 字符的文件名,但并不是所有的客户端都能正确解析这种方式。
因此,如果你只关心基本的兼容性,filename="$encoded_fname"
已经足够了。
相应java后端配置
String realFileName = URLEncoder.encode(realFileName, StandardCharsets.UTF_8.toString());
response.setHeader("Content-Disposition", "attachment; filename=\"" + realFileName + "\"");
所以综上,实际开发的话,前端取第一个filename即可
前端下载文件流并读取响应头中content-disposition
字段作为文件名
const res = await axios({
method: 'get',
url: `/exportData`,
params: {
id: this.id,
},
responseType: 'blob',
});
// post请求
// const res = await http({
// method: 'post',
// url: `/exportData`,
// data: {
// id: this.id,
// },
// responseType: 'blob',
// });
if(!res.data) return
// 获取响应头Content-Disposition中的filename参数,后端需要设置
// content-disposition格式示例:
// "filename=不合格情况统计.xlsx"; filename*=utf-8''不合格情况统计.xlsx"
const fileNameEncode = res.headers['content-disposition']
.split('filename=')[1]
.split(';')[0];
const fileName = decodeURI(fileNameEncode, 'utf-8').replaceAll('"', '');
const blob = new Blob([res.data]);
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.style.display = 'none';
link.setAttribute('href', url);
link.setAttribute('download', fileName);
link.click();
URL.revokeObjectURL(url);