Browser 如何在HTTP中对内容处置头的filename参数进行编码?

Browser 如何在HTTP中对内容处置头的filename参数进行编码?,browser,http-headers,specifications,Browser,Http Headers,Specifications,想要强制下载资源而不是直接在Web浏览器中呈现资源的Web应用程序在表单的HTTP响应中发出内容处置标题: 内容配置:附件;filename=文件名 filename参数可用于建议浏览器将资源下载到的文件的名称。但是,(内容处置)在(文件名参数)中指出,文件名只能使用US-ASCII字符: 当前[RFC 2045]语法限制 参数值(因此 内容处置(文件名)到 US-ASCII。我们承认伟大的 允许任意行为的可取性 文件名中的字符集,但它是 超出本文件的范围 确定必要的机制 然而,有经验证据表明,

想要强制下载资源而不是直接在Web浏览器中呈现资源的Web应用程序在表单的HTTP响应中发出
内容处置
标题:

内容配置:附件;filename=文件名

filename
参数可用于建议浏览器将资源下载到的文件的名称。但是,(内容处置)在(文件名参数)中指出,文件名只能使用US-ASCII字符:

当前[RFC 2045]语法限制 参数值(因此 内容处置(文件名)到 US-ASCII。我们承认伟大的 允许任意行为的可取性 文件名中的字符集,但它是 超出本文件的范围 确定必要的机制

然而,有经验证据表明,当今最流行的Web浏览器似乎允许使用非美国ASCII字符,但(由于缺乏标准)在文件名的编码方案和字符集规范上存在分歧。问题是,如果文件名“naïvefile”(不带引号,其中第三个字母是U+00EF)需要编码到内容处理头中,流行浏览器采用的各种方案和编码是什么

就本问题而言,流行浏览器包括:

  • 火狐
  • Internet Explorer
  • 狩猎
  • 谷歌浏览器
  • 歌剧院

我通常对文件名进行URL编码(使用%xx),而且它似乎在所有浏览器中都能工作。无论如何,您可能需要进行一些测试。

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


表示此类标题应根据上述RFC草案中包含的内容进行编码。

以下文件链接了作者在其回答中提到的内容,进一步解决了该问题,并在此处明确指出:

  • 内容处置
    中没有可互操作的编码非ASCII名称的方法

  • 内容配置
    中使用UTF-8的方法非常奇怪:
    文件名*=UTF-8“foo%c3%a4
    (是的,这是一个星号,中间只有一个空引号,没有引号)

  • 这个标题有点不太标准(,但不需要客户端支持)

有一个简单且非常健壮的替代方法:使用包含所需文件名的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)

我知道这是一个老帖子,但它仍然非常相关。我发现现代浏览器支持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和更早版本也不支持,您需要使用utf-8编码的IE标准,百分比编码:

Content-Disposition: attachment; filename=Na%C3%AFve%20file.txt
在ASP.Net中,我使用以下代码:

字符串内容配置;
if(Request.Browser.Browser==“IE”&&(Request.Browser.Version==“7.0”| | Request.Browser.Version==“8.0”))
contentDisposition=“附件;文件名=“+Uri.EscapeDataString(文件名);
else if(Request.Browser.Browser==“Safari”)
contentDisposition=“附件;文件名=”+文件名;
其他的
contentDisposition=“附件;文件名*=UTF-8”“”+Uri.EscapeDataString(文件名);
AddHeader(“内容处置”,contentDisposition);
我使用IE7、IE8、IE9、Chrome 13、Opera 11、FF5和Safari 5测试了上述内容

更新2013年11月:

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

字符串内容配置;
if(Request.Browser.Browser==“IE”&&(Request.Browser.Version==“7.0”| | Request.Browser.Version==“8.0”))
contentDisposition=“附件;文件名=“+Uri.EscapeDataString(文件名);
else if(Request.UserAgent!=null&&Request.UserAgent.ToLowerInvariant().Contains(“android”)//android内置下载管理器(android上的所有浏览器)
contentDisposition=“附件;文件名=\”“+MakeAndroidSafeFileName(文件名)+\”;
其他的
contentDisposition=“附件;文件名=\”“+filename+”\“文件名*=UTF-8”+Uri.EscapeDataString(文件名);
AddHeader(“内容处置”,contentDisposition);
以上内容现已在IE7-11、Chrome 32、Opera 12、FF25、Safari 6中测试,使用此文件名下载:你好abcABCæåØäýýýñ½§#¤%&()=`@£$€{[]}+´¨^~'-_,;.文本

在IE7上,它适用于某些字符,但不是所有字符。但如今谁在乎IE7呢

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

private static readonly Dictionary AndroidAllowedChars=“abcdefghijklmnopqrstuvwxyzabdfghijklmnopqrstuvxyz.-+,@$€!½§~'=()[]{0123456789.”ToDictionary(c=>c);
私有字符串MakeAndroidSafeFileName(字符串文件名)
Content-Disposition: attachment;
                     filename="EURO rates";
                     filename*=utf-8''%e2%82%ac%20rates
$il1_filename = utf8_decode($filename);
$to_underscore = "\"\\#*;:|<>/?";
$safe_filename = strtr($il1_filename, $to_underscore, str_repeat("_", strlen($to_underscore)));

header("Content-Disposition: attachment; filename=\"$safe_filename\""
.( $safe_filename === $filename ? "" : "; filename*=UTF-8''".rawurlencode($filename) ));
public static class HttpRequestMessageExtensions
{
    public static HttpResponseMessage CreateFileResponse(this HttpRequestMessage request, byte[] data, string filename, string mediaType)
    {
        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
        var stream = new MemoryStream(data);
        stream.Position = 0;

        response.Content = new StreamContent(stream);

        response.Content.Headers.ContentType = 
            new MediaTypeHeaderValue(mediaType);

        // URL-Encode filename
        // Fixes behavior in IE, that filenames with non US-ASCII characters
        // stay correct (not "_utf-8_.......=_=").
        var encodedFilename = HttpUtility.UrlEncode(filename, Encoding.UTF8);

        response.Content.Headers.ContentDisposition =
            new ContentDispositionHeaderValue("attachment") { FileName = encodedFilename };
        return response;
    }
}
Content-Disposition: attachment; filename="My Report.doc"
header('Content-Disposition: attachment;'
    . 'filename="' . addslashes(utf8_decode($filename)) . '";'
    . 'filename*=utf-8\'\'' . rawurlencode($filename));
$filenameFallback = preg_replace('#^.*\.#', md5($filename) . '.', $filename);
$disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $filename, $filenameFallback);
$response->headers->set('Content-Disposition', $disposition);