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


当前回答

PHP框架Symfony 4在HeaderUtils::makeDisposition中有$filenameFallback。 您可以查看这个函数的详细信息-它与上面的答案类似。

使用的例子:

$filenameFallback = preg_replace('#^.*\.#', md5($filename) . '.', $filename);
$disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $filename, $filenameFallback);
$response->headers->set('Content-Disposition', $disposition);

其他回答

我知道这是一个老帖子,但它仍然非常相关。我发现现代浏览器支持rfc5987,它允许utf-8编码,百分比编码(url编码)。然后Naïve file.txt变成:

Content-Disposition: attachment; filename*=UTF-8''Na%C3%AFve%20file.txt

Safari(5)不支持这一点。相反,你应该使用Safari标准,直接在utf-8编码的头文件中写入文件名:

Content-Disposition: attachment; filename=Naïve file.txt

IE8及以上版本也不支持,你需要使用IE标准的utf-8编码,百分比编码:

Content-Disposition: attachment; filename=Na%C3%AFve%20file.txt

在ASP。Net我使用以下代码:

string contentDisposition;
if (Request.Browser.Browser == "IE" && (Request.Browser.Version == "7.0" || Request.Browser.Version == "8.0"))
    contentDisposition = "attachment; filename=" + Uri.EscapeDataString(fileName);
else if (Request.Browser.Browser == "Safari")
    contentDisposition = "attachment; filename=" + fileName;
else
    contentDisposition = "attachment; filename*=UTF-8''" + Uri.EscapeDataString(fileName);
Response.AddHeader("Content-Disposition", contentDisposition);

我用IE7、IE8、IE9、Chrome 13、Opera 11、FF5、Safari 5测试了上述内容。

2013年11月更新:

这是我目前使用的代码。我仍然必须支持IE8,所以我不能摆脱第一部分。事实证明,Android上的浏览器使用内置的Android下载管理器,它不能可靠地以标准方式解析文件名。

string contentDisposition;
if (Request.Browser.Browser == "IE" && (Request.Browser.Version == "7.0" || Request.Browser.Version == "8.0"))
    contentDisposition = "attachment; filename=" + Uri.EscapeDataString(fileName);
else if (Request.UserAgent != null && Request.UserAgent.ToLowerInvariant().Contains("android")) // android built-in download manager (all browsers on android)
    contentDisposition = "attachment; filename=\"" + MakeAndroidSafeFileName(fileName) + "\"";
else
    contentDisposition = "attachment; filename=\"" + fileName + "\"; filename*=UTF-8''" + Uri.EscapeDataString(fileName);
Response.AddHeader("Content-Disposition", contentDisposition);

上面现在测试在IE7-11, Chrome 32,歌剧12日FF25, Safari 6,使用该文件名下载:你好abcABCæø一ÆØAaouieeiaeiaouyn ½§!#¤%&()=`@£$ € {[]}+´¨^~'-_,;. 三种

在IE7上,它适用于某些字符,但不是所有字符。但是现在谁还关心IE7呢?

这是我用来为Android生成安全文件名的函数。注意,我不知道Android支持哪些字符,但我已经测试过了,这些字符肯定有效:

private static readonly Dictionary<char, char> AndroidAllowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._-+,@£$€!½§~'=()[]{}0123456789".ToDictionary(c => c);
private string MakeAndroidSafeFileName(string fileName)
{
    char[] newFileName = fileName.ToCharArray();
    for (int i = 0; i < newFileName.Length; i++)
    {
        if (!AndroidAllowedChars.ContainsKey(newFileName[i]))
            newFileName[i] = '_';
    }
    return new string(newFileName);
}

@TomZ:我在IE7和IE8中进行了测试,结果证明我不需要转义撇号(')。你能举个失败的例子吗?

@Dave Van den Eynde:根据RFC6266将两个文件名合并在一行中,除了Android和IE7+8,我已经更新了代码来反映这一点。谢谢你的建议。

@Thilo:不知道GoodReader或其他非浏览器。使用Android方法可能会有一些运气。

@Alex Zhukovskiy:我不知道为什么,但正如在Connect上讨论的那样,它似乎运行得不太好。

在PHP中,这为我做了(假设文件名是UTF8编码):

header('Content-Disposition: attachment;'
    . 'filename="' . addslashes(utf8_decode($filename)) . '";'
    . 'filename*=utf-8\'\'' . rawurlencode($filename));

在IE8-11、Firefox和Chrome浏览器上进行测试。 如果浏览器可以解释文件名*=utf-8,它将使用文件名的UTF8版本,否则它将使用解码后的文件名。如果你的文件名包含的字符不能在ISO-8859-1中表示,你可能要考虑使用iconv代替。

PHP框架Symfony 4在HeaderUtils::makeDisposition中有$filenameFallback。 您可以查看这个函数的详细信息-它与上面的答案类似。

使用的例子:

$filenameFallback = preg_replace('#^.*\.#', md5($filename) . '.', $filename);
$disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $filename, $filenameFallback);
$response->headers->set('Content-Disposition', $disposition);

在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

在asp.net mvc2中,我使用这样的东西:

return File(
    tempFile
    , "application/octet-stream"
    , HttpUtility.UrlPathEncode(fileName)
    );

我想如果你不使用mvc(2),你可以只编码文件名使用

HttpUtility.UrlPathEncode(fileName)