想要强制下载资源而不是直接在Web浏览器中呈现资源的Web应用程序Content-Disposition
在表单的HTTP响应中发出标头:
Content-Disposition: attachment; filename=FILENAME
该filename
参数可用于建议浏览器下载资源的文件的名称.但是,RFC 2183(Content-Disposition)在2.3节(文件名参数)中指出文件名只能使用US-ASCII字符:
当前[RFC 2045]语法将参数值(以及因此内容处理文件名)限制为US-ASCII.我们认识到允许在文件名中使用任意字符集的巨大愿望,但是定义必要的机制超出了本文档的范围.
然而,有经验证据表明,当今大多数流行的Web浏览器似乎都允许非US-ASCII字符(缺乏标准)对编码方案和文件名的字符集规范不同意.问题是,如果文件名"naïvefile"(没有引号,第三个字母是U + 00EF)需要编码到Content-Disposition标题中,那么流行浏览器采用的各种方案和编码是什么?
出于这个问题的目的,流行的浏览器是:
火狐
IE浏览器
苹果浏览器
谷歌浏览器
歌剧
Martin Ørdin.. 350
我知道这是一个老帖子,但它仍然非常相关.我发现现代浏览器支持rfc5987,它允许utf-8编码,百分比编码(url编码).然后Naïvefile.txt变成:
Content-Disposition: attachment; filename*=UTF-8''Na%C3%AFve%20file.txt
Safari(5)不支持此功能.相反,您应该使用直接在utf-8编码标头中编写文件名的Safari标准:
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中测试,使用此文件名下载:你好abcABCæøåÆØÅäöüïëêîâéíáóúýñ½§!#¤%&()=`@£$€{[]} +' ^〜'-_,;.TXT
在IE7上,它适用于某些字符但不是全部.但是谁现在关心IE7呢?
这是我用来为Android生成安全文件名的函数.请注意,我不知道Android上支持哪些字符,但我已经测试了这些字符的确有效:
private static readonly Dictionary 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上所讨论的那样,它看起来效果不是很好.
我知道这是一个老帖子,但它仍然非常相关.我发现现代浏览器支持rfc5987,它允许utf-8编码,百分比编码(url编码).然后Naïvefile.txt变成:
Content-Disposition: attachment; filename*=UTF-8''Na%C3%AFve%20file.txt
Safari(5)不支持此功能.相反,您应该使用直接在utf-8编码标头中编写文件名的Safari标准:
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中测试,使用此文件名下载:你好abcABCæøåÆØÅäöüïëêîâéíáóúýñ½§!#¤%&()=`@£$€{[]} +' ^〜'-_,;.TXT
在IE7上,它适用于某些字符但不是全部.但是谁现在关心IE7呢?
这是我用来为Android生成安全文件名的函数.请注意,我不知道Android上支持哪些字符,但我已经测试了这些字符的确有效:
private static readonly Dictionary 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上所讨论的那样,它看起来效果不是很好.
没有可互操作的方法来编码非ASCII名称Content-Disposition
.浏览器兼容性很乱.
使用UTF-8 的理论上正确的语法Content-Disposition
非常奇怪:( filename*=UTF-8''foo%c3%a4
是的,这是一个星号,除了中间的空单引号外没有引号)
这个标题有点不太标准(HTTP/1.1规范承认它存在,但不要求客户端支持它).
有一个简单且非常强大的替代方法:使用包含所需文件名的URL.
当最后一个斜杠后面的名称是你想要的名字时,你不需要任何额外的标题!
这个技巧有效:
/real_script.php/fake_filename.doc
如果您的服务器支持URL重写(例如mod_rewrite
在Apache中),那么您可以完全隐藏脚本部分.
URL中的字符应为UTF-8,逐字节urlencoded:
/mot%C3%B6rhead # motörhead
在提出的RFC 5987 "用于超文本传输协议(HTTP)标头字段参数的字符集和语言编码"中讨论了这一点,包括浏览器测试和向后兼容性的链接.
RFC 2183表明此类标头应根据RFC 2184进行编码,RFC 2184已被上述RFC草案所涵盖.
RFC 6266描述了" 在超文本传输协议(HTTP)中使用内容 - 处置标头字段 ".引用:
6.国际化考虑因素
使用[ RFC5987 ]中定义的编码的"
filename*
"参数(第4.3节)允许服务器传输ISO-8859-1字符集之外的字符,还可以选择指定正在使用的语言.
在他们的示例部分中:
此示例与上面的示例相同,但添加"filename"参数是为了与未实现RFC 5987的用户代理兼容 :
Content-Disposition: attachment; filename="EURO rates"; filename*=utf-8''%e2%82%ac%20rates注意:那些不支持RFC 5987编码的用户代理
filename*
在"filename
" 之后发生时忽略" ".
在附录D中,还有一长串建议可以提高互操作性.它还指向一个比较实现的站点.适用于常见文件名的当前全通测试包括:
attwithisofnplain:带有双引号且无编码的普通ISO-8859-1文件名.这需要一个文件名,该文件名都是ISO-8859-1,并且不包含百分号,至少不在十六进制数字前面.
attfnboth:上述顺序中的两个参数.应该适用于大多数浏览器上的大多数文件名,尽管IE8将使用" filename
"参数.
这RFC 5987又引用了RFC 2231,它描述了实际的格式.2231主要用于邮件,5987告诉我们哪些部分也可用于HTTP标头.不要将此与multipart/form-data
HTTP 主体内使用的MIME标头混淆,后者由RFC 2388(特别是第4.4节)和HTML 5草案管理.
以下文件与Jim在他的回答中提到的RFC草案相关联,进一步解决了这个问题,这里绝对值得直接注意:
HTTP Content-Disposition标头和RFC 2231/2047编码的测试用例
在asp.net mvc2我使用这样的东西:
return File( tempFile , "application/octet-stream" , HttpUtility.UrlPathEncode(fileName) );
我想如果你不使用mvc(2)你可以使用编码文件名
HttpUtility.UrlPathEncode(fileName)
我使用以下代码片段进行编码(假设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 + "\"");
在ASP.NET Web API中,我url编码文件名:
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"
http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download
我在所有主流浏览器中测试了以下代码,包括较旧的浏览器(通过兼容模式),它适用于所有地方:
$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.'"');
如果您使用的是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);
}