如何编码名参数的Content-Disposition header在HTTP?
-
01-07-2019 - |
题
网络应用程序,希望以武力资源 下载 而不是直接 呈现 在一个网络浏览器问题的一个 Content-Disposition
头在HTTP响应的形式:
Content-Disposition: attachment; filename=文件名
的 filename
参数可用来表明文件的名称其资源被下载通过浏览器。 RFC2183 (Content-Disposition),但是,国 2.3节 (名参数),文件名称只能使用US-ASCII characters:
目前[RFC2045]法限制 参数的价值(因 Content-Disposition文件)来 US-ASCII.我们认识到伟大的 是否应允许任意 字符集在文件名,但是它是 超出了本文件的范围, 定义的必要机制。
有经验证据,尽管如此,最受欢迎的网络浏览器今天似乎允许non-US-ASCII characters但(为缺乏标准)不同意编码方案和特定规范的文件名称。问题是,什么是各种计划和编码中采用的流行浏览器,如果该文件名称"naïvefile"(没有报价,并在第三封信是U+00EF)需要编入Content-Disposition header?
对于这个问题的目的, 受欢迎的浏览器 正:
- Firefox
- Internet Explorer
- Safari
- 谷歌铬
- 歌剧
其他提示
我知道这是一个古老的职位,但它仍然是非常相关的。我们发现,现代浏览器支持rfc5987,它允许utf-8编码、编码的百分比(url-编码)。那么天真file.txt 变成:
Content-Disposition: attachment; filename*=UTF-8''Na%C3%AFve%20file.txt
Safari(5)不支持这一点。而不是你应该使用野生动物标准编写的文件名称直接在你的utf-8编码头:
Content-Disposition: attachment; filename=Naïve file.txt
IE8和年长不支持它,你要使用即标准的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、铬13、歌剧11,FF5,Safari5.
更新 日2013年:
这里是代码我目前使用的。我仍然必须支持IE8,所以我无法摆脱的第一部分。事实证明,浏览器上安卓的使用建立在安卓载管理,它无法可靠地分析文件的名称,在标准方式。
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、铬32、歌剧12,FF25,Safari6,使用这个文件下载:你好abcABCæøåÆØÅäöüïëêîâéíáóúýñ½§!#¤%&()=`@£$€{[]}+^~'-_,;.txt
在IE7它适用于某些人物但不是全部。但是谁在乎IE7今天?
这是我的使用产生安全的文件名称序。注意,我不知道哪些人物都支持安卓,但我已经测试这些工作肯定的:
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);
}
@这:我测试了在IE7和IE8和事实证明,我不需要逃脱撇号(')。你有一个例子,它失败?
@Dave Van den Eynde:结合这两个文件上的姓名一行为根据RFC6266工作除和IE7+8和我已经更新了代码,以反映这一点。谢谢你的建议。
@蒂:不知道GoodReader或任何其他非浏览器。你可能会有一些幸运的使用的方法。
@亚历克斯茹科夫斯基:我不知道为什么但如上讨论过 连接 它似乎没有工作非常好。
没有互操作方法进行编码的非ASCII的名字
Content-Disposition
. 浏览器的兼容性是一个烂摊子.的 理论上正确的语法 使用的UTF-8
Content-Disposition
是非常的怪异:filename*=UTF-8''foo%c3%a4
(是的,这是一个星号,并且没有报价,除了一个空的单一的报价在中)这头是有点-不-相当标准(HTTP/1.1规范承认它的存在, 但不需要客户,以支持它)。
有一个简单和非常棒的选择: 使用一个网址,包含该文件你想要的.
当名称之后的最后一削减是一个你想要的,你不需要任何额外头!
这一招的工作:
/real_script.php/fake_filename.doc
如果你的服务器支持网址的改写(例如 mod_rewrite
在Apache)然后,你可以完全隐藏的剧本的一部分。
字符的Url应在UTF-8,urlencoded逐字节:
/mot%C3%B6rhead # motörhead
RFC6266 描述了"使用的Content-Disposition Header领域中的超文本传输协议(HTTP)".引用于:
该"
filename*
"参数(4.3节),使用的编码的定义 在[RFC5987],允许服务器发射人物之外的 ISO-8859-1的字符组,并且还可选择指定的语言 在使用。
并在他们的 例部分:
这个例子是同一个以上,但添加"filename" 参数的相容性与用户代理不行 RFC5987立方:
Content-Disposition: attachment; filename="EURO rates"; filename*=utf-8''%e2%82%ac%20rates
注:这些用户代理不支持 RFC5987立方 编码 忽视"
filename*
"当它发生后"filename
”.
在 附录D 还有一个长长的清单的建议,增加互操作性。它还指在 一个网站,其比较实现.目前所有通过测试适用于共同文件名称中包括:
- attwithisofnplain:纯ISO-8859-1文件中的名称与双引号中并没有编码。这就要求文件的名称是所有的ISO-8859-1和不包含%的迹象,至少不会在前六个数字。
- attfnboth:两个参数以上所述。应工作对于大多数文件上的姓名最浏览器,虽然IE8会使用的"
filename
"参数。
那 RFC5987立方 反过来引用 RFC2231, ,其中描述了实际的格式。2231主要是用于邮寄,并5987立方告诉我们哪些部分可能被用于HTTP头。不要混淆这MIME header内部使用 multipart/form-data
HTTP 身体, ,这是由 RFC2388 (第4.4节 特别是)和 HTML5草案.
在asp.net mvc2我用这样的事情:
return File(
tempFile
, "application/octet-stream"
, HttpUtility.UrlPathEncode(fileName)
);
我猜如果你不使用视(2)你可以只进行编码使用的文件名
HttpUtility.UrlPathEncode(fileName)
把文件名称中的双引号。解决了这个问题对于我。是这样的:
Content-Disposition: attachment; filename="My Report.doc"
http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download
我已经测试了多种选择。浏览器不支持规范和不同的行为,我相信双引号是最好的选择。
我使用了下列代码段用于编码(假设 文件名 包含的文件和扩展的文件,即: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 + "\"");
在ASP.NET 网API,我url编码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;
}
}
我测试了下列代码,在所有主要的浏览器,包括老年探险者(通过的兼容性模式),它工作得很好到处:
$filename = $_GET['file']; //this string from $_GET is already decoded
if (strstr($_SERVER['HTTP_USER_AGENT'],"MSIE"))
$filename = rawurlencode($filename);
header('Content-Disposition: attachment; filename="'.$filename.'"');
如果您使用的是一个node.js后你可以用下面的代码,我发现 在这里,
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);
}
我结束了下列代码在我的"download.php"脚本(根据 该博文 和 这些试验案例).
$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) ));
这种使用的标准方法filename="..."只要有只异latin1和"安全"字的使用;如果没有,它增加了该文件*=UTF-8"网址的编码的方式。根据 这一特定情况下测试, 它应该从MSIE9,并在最近FF、铬、野生动物园;在下MSIE版本,它应该提供的文件包含ISO8859-1版本的文件,强调在符不在这个编码。
最后的注:最大。大小的每个标题领域是8190字节apache.UTF-8可以将多达四个字每个字符;后rawurlencode,它是x3=12个字节每一个字符。漂亮的效率不高,但它仍然应该理论上可能有600多个"微笑"%F0%9%98%81的文件。
在PHP这没有对我的(假定该文件是UTF8编码):
header('Content-Disposition: attachment;'
. 'filename="' . addslashes(utf8_decode($filename)) . '";'
. 'filename*=utf-8\'\'' . rawurlencode($filename));
测试对IE8-11,火狐和铬。
如果浏览器可以解释 文件*=utf-8 它将使用UTF8版本的文件,否则它会使用解码文件。如果你的文件中包含的字符,不可表示在ISO-8859-1你可能想要考虑使用 iconv
代替。
经典ASP解决方案
大多数现代浏览器支持通过 Filename
作为 UTF-8
但现在就是这种情况的文件上载解决方案我用的是基于 FreeASPUpload.Net (站点不再存在,链接点 archive.org) 它不会的工作为分析的二进制依赖于阅读的单字节ASCII encoded strings,它工作得很好,当您通过了UTF-8编码数据的数据直到你得到符ASCII不支持。
但是我能找到一个解决方案得到代码阅读和分析的二进制as UTF-8.
Public Function BytesToString(bytes) 'UTF-8..
Dim bslen
Dim i, k , N
Dim b , count
Dim str
bslen = LenB(bytes)
str=""
i = 0
Do While i < bslen
b = AscB(MidB(bytes,i+1,1))
If (b And &HFC) = &HFC Then
count = 6
N = b And &H1
ElseIf (b And &HF8) = &HF8 Then
count = 5
N = b And &H3
ElseIf (b And &HF0) = &HF0 Then
count = 4
N = b And &H7
ElseIf (b And &HE0) = &HE0 Then
count = 3
N = b And &HF
ElseIf (b And &HC0) = &HC0 Then
count = 2
N = b And &H1F
Else
count = 1
str = str & Chr(b)
End If
If i + count - 1 > bslen Then
str = str&"?"
Exit Do
End If
If count>1 then
For k = 1 To count - 1
b = AscB(MidB(bytes,i+k+1,1))
N = N * &H40 + (b And &H3F)
Next
str = str & ChrW(N)
End If
i = i + count
Loop
BytesToString = str
End Function
信用去 纯ASP文件上传 通过实施 BytesToString()
从功能 include_aspuploader.asp
在我自己的代码我能得到 UTF-8
文件名工作。
有用的链接
只是一个更新,因为我是试图在所有这些东西今天在应对客户问题
- 除野生动物园配置用于日本的,所有的浏览器我们的客户进行测试工作最好与filename=文本。pdf-中文本是一个客户的价值序列化ASP.Net/IIS 在utf-8没有url编码。由于某些原因,野生动物构成为英国将接受并妥善保存一个文件与utf-8日的名称,但这同一浏览器构成对日本将文件保存与utf-8chars未解释.所有其他浏览器进行测试似乎最好的工作/现(无论语言配置)的文件utf-8encoded不url编码。
- 我找不到一个单一的浏览器实施Rfc5987/8187 在所有.我测试了最新的铬,火狐建立加即11和边缘。我试图设置的标题仅仅是文件*=utf-8"texturlencoded.pdf,设置这两filename=文本。pdf;文件*=utf-8"texturlencoded.pdf。没有一个特征Rfc5987/8187似乎得到正确处理的任何上述内容。
我们有一个类似的问题在一个网络应用程序,并最终通过阅读文件从HTML <input type="file">
, ,并设置在网址的编码形式在一个新HTML <input type="hidden">
.当然,我们必须除去的路径就像"C:\fakepath\",也就是返回的一些浏览器。
当然,这并不是直接答案行动的问题,但可能是一个解决办法用于其他人。
我通常URL-encode(与%xx)的文件,并且它似乎在所有的浏览器。你可能想要做一些测试。