可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Web applications that want to force a resource to be downloaded rather than directly rendered in a Web browser issue a Content-Disposition
header in the HTTP response of the form:
Content-Disposition: attachment; filename=FILENAME
The filename
parameter can be used to suggest a name for the file into which the resource is downloaded by the browser. RFC 2183 (Content-Disposition), however, states in section 2.3 (The Filename Parameter) that the file name can only use US-ASCII characters:
Current [RFC 2045] grammar restricts parameter values (and hence Content-Disposition filenames) to US-ASCII. We recognize the great desirability of allowing arbitrary character sets in filenames, but it is beyond the scope of this document to define the necessary mechanisms.
For the purpose of this question, popular browsers being:
- Firefox
- Internet Explorer
- Safari
- Google Chrome
- Opera
回答1:
There is discussion of this, including links to browser testing and backwards compatibility, in the proposed RFC 5987, "Character Set and Language Encoding for Hypertext Transfer Protocol (HTTP) Header Field Parameters."
RFC 2183 indicates that such headers should be encoded according to RFC 2184, which was obsoleted by RFC 2231, covered by the draft RFC above.
回答2:
Content-Disposition: attachment; filename*=UTF-8''Na%C3%AFve%20file.txt
Safari (5) does not support this. Instead you should use the Safari standard of writing the file name directly in your utf-8 encoded header:
IE8 and older don't support it either and you need to use the IE standard of utf-8 encoding, percentage encoded:
Content-Disposition: attachment; filename=Na%C3%AFve%20file.txt
In ASP.Net I use the following code:
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);
I tested the above using IE7, IE8, IE9, Chrome 13, Opera 11, FF5, Safari 5.
Update November 2013:
Here is the code I currently use. I still have to support IE8, so I cannot get rid of the first part. It turns out that browsers on Android use the built in Android download manager and it cannot reliably parse file names in the standard way.
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);
On IE7 it works for some characters but not all. But who cares about IE7 nowadays?
This is the function I use to generate safe file names for Android. Note that I don't know which characters are supported on Android but that I have tested that these work for sure:
@TomZ: I tested in IE7 and IE8 and it turned out that I did not need to escape apostrophe ('). Do you have an example where it fails?
@Dave Van den Eynde: Combining the two file names on one line as according to RFC6266 works except for Android and IE7+8 and I have updated the code to reflect this. Thank you for the suggestion.
@Thilo: No idea about GoodReader or any other non-browser. You might have some luck using the Android approach.
@Alex Zhukovskiy: I don't know why but as discussed on Connect it doesn't seem to work terribly well.
回答3:
There is a simple and very robust alternative: use a URL that contains the filename you want.
When the name after the last slash is the one you want, you don't need any extra headers!
This trick works:
/real_script.php/fake_filename.doc
And if your server supports URL rewriting (e.g. mod_rewrite
in Apache) then you can fully hide the script part.
Characters in URLs should be in UTF-8, urlencoded byte-by-byte:
回答4:
RFC 6266 describes the “Use of the Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP)”. Quoting from that:
6. Internationalization Considerations
The “filename*
” parameter (Section 4.3), using the encoding defined in [RFC5987], allows the server to transmit characters outside the ISO-8859-1 character set, and also to optionally specify the language in use.
And in their examples section:
This example is the same as the one above, but adding the "filename" parameter for compatibility with user agents not implementing RFC 5987:
Content-Disposition: attachment; filename="EURO rates"; filename*=utf-8''%e2%82%ac%20rates
Note: Those user agents that do not support the RFC 5987 encoding ignore “filename*
” when it occurs after “filename
”.
In Appendix D there is also a long list of suggestions to increase interoperability. It also points at a site which compares implementations. Current all-pass tests suitable for common file names include:
- attwithisofnplain: plain ISO-8859-1 file name with double quotes and without encoding. This requires a file name which is all ISO-8859-1 and does not contain percent signs, at least not in front of hex digits.
- attfnboth: two parameters in the order described above. Should work for most file names on most browsers, although IE8 will use the “
filename
” parameter.
That RFC 5987 in turn references RFC 2231, which describes the actual format. 2231 is primarily for mail, and 5987 tells us what parts may be used for HTTP headers as well. Don't confuse this with MIME headers used inside a multipart/form-data
HTTP body, which is governed by RFC 2388 (section 4.4 in particular) and the HTML 5 draft.
回答5:
The following document linked from the draft RFC mentioned by Jim in his answer further addresses the question and definitely worth a direct note here:
Test Cases for HTTP Content-Disposition header and RFC 2231/2047 Encoding
回答6:
in asp.net mvc2 i use something like this:
return File( tempFile , "application/octet-stream" , HttpUtility.UrlPathEncode(fileName) );
I guess if you don't use mvc(2) you could just encode the filename using
HttpUtility.UrlPathEncode(fileName)
回答7:
I use the following code snippets for encoding (assuming fileName contains the filename and extension of the file, i.e.: 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 + "\"");
回答8:
In ASP.NET Web API, I url encode the 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; } }

回答9:
Put you file name in double quotes. Solved the problem for me. Like this:
Content-Disposition: attachment; filename="My Report.doc"
http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download
回答10:
I tested the following code in all major browsers, including older Explorers (via the compatibility mode), and it works well everywhere:
$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.'"');
回答11:
If you are using a nodejs backend you can use the following code I found here
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); }
回答12:
I ended up with the following code in my "download.php" script (based on this blogpost and these test cases).
$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) ));
This uses the standard way of filename="..." as long as there are only iso-latin1 and "safe" characters used; if not, it adds the filename*=UTF-8'' url-encoded way. According to this specific test case, it should work from MSIE9 up, and on recent FF, Chrome, Safari; on lower MSIE version, it should offer filename containing the ISO8859-1 version of the filename, with underscores on characters not in this encoding.
Final note: the max. size for each header field is 8190 bytes on apache. UTF-8 can be up to four bytes per character; after rawurlencode, it is x3 = 12 bytes per one character. Pretty inefficient, but it should still be theoretically possible to have more than 600 "smiles" %F0%9F%98%81 in the filename.
回答13:
In PHP this did it for me (assuming the filename is UTF8 encoded):
header('Content-Disposition: attachment;' . 'filename="' . addslashes(utf8_decode($filename)) . '";' . 'filename*=utf-8\'\'' . rawurlencode($filename));
Tested against IE8-11, Firefox and Chrome.
If the browser can interpret filename*=utf-8 it will use the UTF8 version of the filename, else it will use the decoded filename. If your filename contains characters that can't be represented in ISO-8859-1 you might want to consider using iconv
instead.
回答14:
Classic ASP Solution
Most modern browsers support passing the Filename
as UTF-8
now but as was the case with a File Upload solution I use that was based on FreeASPUpload.Net (site no longer exists, link points to archive.org) it wouldn't work as the parsing of the binary relied on reading single byte ASCII encoded strings, which worked fine when you passed UTF-8 encoded data until you get to characters ASCII doesn't support.
However I was able to find a solution to get the code to read and parse the binary 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 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
Credit goes to Pure ASP File Upload by implementing the BytesToString()
function from include_aspuploader.asp
in my own code I was able to get UTF-8
filenames working.
Useful Links
回答15:
回答16:
I normally URL-encode (with %xx) the filenames, and it seems to work in all browsers. You might want to do some tests anyway.
回答17:
I found out solution, that works for all my browsers (ie. all browsers I have installed - IE8, FF16, Opera 12, Chrome 22).
My solution is described in other thread: Java servlet download filename special characters
My solution is based on the fact, how browsers trying to read value from filename
parameter. If there is no charset specified in the filename
parameter (for example filename*=utf-8''test.xml
) browsers expect that value is encoded in browser's native encoding.
Different browsers expect diffrent native encoding. Usually browser's native encoding is utf-8 (FireFox, Opera, Chrome). But IE's native encoding is Win-1250. (I don't know anything about other browsers.)
Hence, if we put value into filename
parametr, that is encoded by utf-8/win-1250 according to user's browser, it should work. At least, it works for me.
In short, if we have file named
,
for FireFox, Opera and Chrome I response this header (encoded in utf-8):
and for IE I response this header (encoded in win-1250):
Content-Disposition: attachment; filename="omáèka.jpg"
Java example is in my post that is mentioned above.