如何编码内容的文件名参数

希望强制下载资源而不是直接在Web浏览器中呈现资源的Web应用程序在表单的HTTP响应中发出Content-Disposition标头:

Content-Disposition: attachment; filename=FILENAME

filename参数可用于为浏览器下载资源的文件建议名称。 但RFC 2183(Content-Disposition)在第2.3节(文件名参数)中声明文件名只能使用US-ASCII字符:

目前[RFC 2045]语法将参数值(以及Content-Disposition文件名)限制为US-ASCII。 我们认识到允许在文件名中使用任意字符集的强烈愿望,但是定义必要的机制超出了本文档的范围。

尽管如此,仍有经验证据表明,目前大多数流行的Web浏览器似乎仍允许非US-ASCII字符(对于缺乏标准)对文件名的编码方案和字符集规范持不同意见。 问题是,如果需要将文件名“naïvefile”(不带引号且第三个字母是U + 00EF)编码到Content-Disposition标头中,那么常用浏览器使用的各种方案和编码是什么?

为了这个问题的目的,流行的浏览器是:

  • 火狐
  • IE浏览器
  • 苹果浏览器
  • 谷歌浏览器
  • 歌剧

  • 在提议的RFC 5987“用于超文本传输​​协议(HTTP)头字段参数的字符集和语言编码”中讨论了这一点,包括对浏览器测试和向后兼容性的链接。

    RFC 2183指出,这样的头文件应该根据RFC 2184进行编码,而RFC 2184已经被RFC 2231废弃,该文件已经被上面的RFC草案所涵盖。


    我知道这是一个旧帖子,但它仍然非常相关。 我发现现代浏览器支持rfc5987,它允许utf-8编码,百分比编码(url编码)。 然后Naïvefile.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和更旧版本不支持它,你需要使用utf-8编码的IE标准,百分比编码:

    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,Opera 12,FF25,Safari 6中测试过,使用这个文件名进行下载:你好! ^〜'-_,;。TXT

    在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方法可能会带来一些好运。

    @亚历克斯·朱可夫斯基:我不知道为什么,但正如Connect上所讨论的那样,它看起来不太好。


  • 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,逐字节为urlencode:

    /mot%C3%B6rhead   # motörhead
    
    链接地址: http://www.djcxy.com/p/3771.html

    上一篇: How to encode the filename parameter of Content

    下一篇: Binary Data in JSON String. Something better than Base64