想要强制下载资源而不是直接在Web浏览器中呈现资源的Web应用程序在表单的HTTP响应中发出Content-Disposition报头:
Content-Disposition:附件;filename = filename
filename参数可用于建议浏览器将资源下载到的文件的名称。然而,RFC 2183 (Content-Disposition)在2.3节(文件名参数)中规定文件名只能使用US-ASCII字符:
当前[RFC 2045]语法限制
参数值(因此
内容-处置文件名)到
us - ascii。我们认可伟大的
允许任意的可取性
文件名中的字符集,但它是
超出了本文档的范围
定义必要的机制。
然而,有经验证据表明,目前大多数流行的Web浏览器似乎允许非us - ascii字符,但(由于缺乏标准)在文件名的编码方案和字符集规范上存在分歧。问题是,如果文件名“naïvefile”(不带引号,第三个字母是U+00EF)需要编码到Content-Disposition报头中,那么流行的浏览器采用了哪些不同的方案和编码?
为了解决这个问题,流行的浏览器是:
谷歌Chrome
Safari
Internet Explorer或Edge
火狐
歌剧
我使用以下代码片段进行编码(假设fileName包含文件的文件名和扩展名,即:test.txt):
PHP:
if ( strpos ( $_SERVER [ 'HTTP_USER_AGENT' ], "MSIE" ) > 0 )
{
header ( 'Content-Disposition: attachment; filename="' . rawurlencode ( $fileName ) . '"' );
}
else
{
header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode ( $fileName ) );
}
Java:
fileName = request.getHeader ( "user-agent" ).contains ( "MSIE" ) ? URLEncoder.encode ( fileName, "utf-8") : MimeUtility.encodeWord ( fileName );
response.setHeader ( "Content-disposition", "attachment; filename=\"" + fileName + "\"");
RFC 6266描述了“超文本传输协议(HTTP)中内容处理报头字段的使用”。引用其中的话:
6. 国际化的考虑
参数" filename* "(章节4.3),使用定义的编码
在[RFC5987]中,允许服务器传输外部的字符
ISO-8859-1字符集,也可以选择指定语言
在使用。
在例子部分:
这个示例与上面的示例相同,但添加了"filename"
参数,用于与未实现的用户代理的兼容性
RFC 5987:
附加:附件;
文件名= "欧元利率”;
文件名* = utf - 8”% e2 % 82% ac % 20率
注意:不支持RFC 5987编码的用户代理
当" filename "后面出现" filename* "时,忽略" filename* "。
在附录D中,还列出了一长串提高互操作性的建议。它还指向一个比较实现的站点。适用于常用文件名的当前全通过测试包括:
attwithisofnplain:普通的ISO-8859-1文件名,双引号,不带编码。这要求文件名完全符合ISO-8859-1,并且不包含百分号,至少在十六进制数字前面不包含百分号。
Attfnboth:上述顺序的两个参数。应该适用于大多数浏览器上的大多数文件名,尽管IE8将使用" filename "参数。
RFC 5987又引用了描述实际格式的RFC 2231。2231主要用于邮件,5987告诉我们哪些部分也可以用于HTTP报头。不要将其与多部分/form-data HTTP主体中使用的MIME头相混淆,后者受RFC 2388(特别是4.4节)和HTML 5草案的约束。
如果你使用的是nodejs后端,你可以使用我在这里找到的以下代码
var fileName = 'my file(2).txt';
var header = "Content-Disposition: attachment; filename*=UTF-8''"
+ encodeRFC5987ValueChars(fileName);
function encodeRFC5987ValueChars (str) {
return encodeURIComponent(str).
// Note that although RFC3986 reserves "!", RFC5987 does not,
// so we do not need to escape it
replace(/['()]/g, escape). // i.e., %27 %28 %29
replace(/\*/g, '%2A').
// The following are not required for percent-encoding per RFC5987,
// so we can allow for a little better readability over the wire: |`^
replace(/%(?:7C|60|5E)/g, unescape);
}
在Content-Disposition中没有可互操作的方法来编码非ascii名称。浏览器兼容性是一团糟。
在Content-Disposition中使用UTF-8的理论上正确的语法是非常奇怪的:filename*=UTF-8 " foo%c3%a4(是的,这是一个星号,没有引号,除了中间的一个空单引号)
这个报头有点不太标准(HTTP/1.1规范承认它的存在,但不要求客户端支持它)。
有一种简单而可靠的替代方法:使用包含所需文件名的URL。
当最后一个斜杠后面的名称是您想要的名称时,您不需要任何额外的头文件!
这个技巧很管用:
/real_script.php/fake_filename.doc
如果你的服务器支持URL重写(例如Apache中的mod_rewrite),那么你可以完全隐藏脚本部分。
url中的字符应该是UTF-8,逐字节url编码:
/mot%C3%B6rhead # motörhead