想要强制下载资源而不是直接在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 火狐 歌剧


当前回答

以下文件链接自Jim在回答中提到的RFC草案,进一步解决了这个问题,在这里值得直接注意:

HTTP内容处理头和rfc2231 /2047编码的测试用例

其他回答

只是一个更新,因为我今天为了回应一个客户的问题而尝试了所有这些东西

With the exception of Safari configured for Japanese, all browsers our customer tested worked best with filename=text.pdf - where text is a customer value serialized by ASP.Net/IIS in utf-8 without url encoding. For some reason, Safari configured for English would accept and properly save a file with utf-8 Japanese name but that same browser configured for Japanese would save the file with the utf-8 chars uninterpreted. All other browsers tested seemed to work best/fine (regardless of language configuration) with the filename utf-8 encoded without url encoding. I could not find a single browser implementing Rfc5987/8187 at all. I tested with the latest Chrome, Firefox builds plus IE 11 and Edge. I tried setting the header with just filename*=utf-8''texturlencoded.pdf, setting it with both filename=text.pdf; filename*=utf-8''texturlencoded.pdf. Not one feature of Rfc5987/8187 appeared to be getting processed correctly in any of the above.

我使用以下代码片段进行编码(假设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 + "\"");

以下文件链接自Jim在回答中提到的RFC草案,进一步解决了这个问题,在这里值得直接注意:

HTTP内容处理头和rfc2231 /2047编码的测试用例

在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

如果你使用的是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);
}