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」(引用符なし、3文字目がU + 00EF)をContent-Dispositionヘッダーにエンコードする必要がある場合、一般的なブラウザで採用されているさまざまな方式とエンコードは何ですか?
この質問の目的のために、人気のあるブラウザは、
私はこれが古い投稿であることを知っていますが、それはまだ非常に関連性があります。私は最近のブラウザがUTF-8エンコーディング、パーセンテージエンコード(URLエンコード)を可能にするrfc5987をサポートすることを発見しました。そしてナイーブなfile.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以前でもそれをサポートしていないので、IE標準の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、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:AndroidおよびIE7 + 8を除き、RFC 6266に準拠して2つのファイル名を1行に組み合わせることで、これを反映するようにコードを更新しました。ご提案ありがとうございます。
@Thilo:GoodReaderや他のブラウザ以外のものについては全く考えていません。あなたはAndroidのアプローチを使って運があるかもしれません。
@Alex Zhukovskiy:理由はわかりませんが、 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で、バイトごとにURLエンコードされている必要があります。
/mot%C3%B6rhead # motörhead
RFC 6266 「 ハイパーテキスト転送プロトコル(HTTP)でのContent-Dispositionヘッダーフィールドの使用 」について説明しています。それから引用する:
[ RFC5987 ]で定義されているエンコーディングを使用する“
filename*
”パラメータ( セクション4. )を使用すると、サーバーはISO-8859-1文字セット以外の文字を送信したり、オプションで言語を指定できます使用中で。
そして彼らの 例のセクション :
この例は上の例と同じですが、 RFC 5987 を実装していないユーザエージェントとの互換性のために "filename"パラメータを追加します。
Content-Disposition: attachment; filename="EURO rates"; filename*=utf-8''%e2%82%ac%20rates
注: RFC 5987 エンコーディングをサポートしていないユーザーエージェントは、 "
filename
"の後にある場合は "filename*
"を無視します。
に 付録D 相互運用性を高めるための提案の長いリストもあります。それはまた指摘します 実装を比較するサイト 。一般的なファイル名に適した現在の全パステストには、次のものがあります。
filename
”パラメータを使用しますが、ほとんどのブラウザでほとんどのファイル名で機能するはずです。それ RFC 5987 次に参照する RFC 2231 、これは実際のフォーマットを記述しています。 2231は主にメール用で、5987はどの部分がHTTPヘッダーにも使用できるかを教えてくれます。これをmultipart/form-data
HTTP body の中で使われているMIMEヘッダと混同しないでください。 RFC 2388 ( セクション4.4 と HTML 5ドラフト 。
asp.netのmvc2で私はこのようなものを使う:
return File(
tempFile
, "application/octet-stream"
, HttpUtility.UrlPathEncode(fileName)
);
Mvc(2)を使用していないのであれば、単にファイル名を使用してエンコードできます。
HttpUtility.UrlPathEncode(fileName)
ファイル名を二重引用符で囲みます。私のために問題を解決しました。このような:
Content-Disposition: attachment; filename="My Report.doc"
http://kb.mozillazine.org/ファイル名_with_spaces_are_truncated_upon_download
私は複数のオプションをテストしました。ブラウザは仕様をサポートしていないため、動作が異なります。二重引用符が最善の選択肢であると考えています。
エンコードには次のコードスニペットを使用します(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では、ファイル名をエンコードします。
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;
}
}
もし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);
}
私は、古いエクスプローラを含むすべての主要ブラウザで(互換モードを介して)次のコードをテストしましたが、どこでも問題なく動作します。
$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.'"');
私は "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) ));
これは、iso-latin1と "safe"の文字しか使用されていない限り、標準のfilename = "..."の方法を使用します。そうでなければ、ファイル名* = UTF-8 ''をURLエンコードした方法を追加します。 この特定のテストケース によれば、それはMSIE9から、そして最近のFF、Chrome、Safariで動作するはずです。それより前のMSIEバージョンでは、ISO8859-1バージョンのファイル名を含むfilenameを提供し、このエンコーディングにない文字にはアンダースコアを付けます。
最後の注意:最大。各ヘッダーフィールドのサイズは、Apacheでは8190バイトです。 UTF-8は1文字あたり最大4バイトです。 rawurlencodeの後は、1文字あたりx 3 = 12バイトです。かなり非効率的ですが、それでもファイル名に600以上の "微笑み"%F0%9F%98%81を含めることは理論的に可能です。
PHPでこれは私のためにそれをしました(ファイル名がUTF8でエンコードされていると仮定して):
header('Content-Disposition: attachment;'
. 'filename="' . addslashes(utf8_decode($filename)) . '";'
. 'filename*=utf-8\'\'' . rawurlencode($filename));
IE8-11、FirefoxおよびChromeに対してテスト済み。
ブラウザがfilename * = utf-8と解釈できる場合は、UTF8バージョンのファイル名が使用され、それ以外の場合はデコードされたファイル名が使用されます。ファイル名。ファイル名にISO-8859-1で表現できない文字が含まれている場合は、代わりにiconv
を使用することを検討してください。
PHPフレームワークSymfony 4には、$filenameFallback
にHeaderUtils::makeDisposition
があります。詳細については、この関数を調べることができます-上記の答えに似ています。
使用例:
$filenameFallback = preg_replace('#^.*\.#', md5($filename) . '.', $filename);
$disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $filename, $filenameFallback);
$response->headers->set('Content-Disposition', $disposition);
私が顧客の問題に対応して私が今日これらすべてのことを試みていたのでちょうど更新
最近のほとんどのブラウザはFilename
をUTF-8
として渡すことをサポートしていますが、私が使用しているファイルアップロードソリューションの場合と同様に FreeASPUpload.Net(このサイトはもう存在しません。 archive.org )へのリンクは、シングルバイトの読み取りに依存したバイナリの解析としては機能しませんASCIIエンコードされた文字列。UTF-8でエンコードされたデータを文字に到達するまで渡すとうまく機能しましたASCIIはサポートしていません。
しかし、私はコードを読み取ってバイナリを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ファイルアップロード に自分のコードでinclude_aspuploader.asp
からBytesToString()
関数を実装することでUTF-8
ファイル名を機能させることができました。