Webanwendungen, die das Herunterladen einer Ressource erzwingen möchten und nicht das direkte Rendern in einem Webbrowser einen Content-Disposition
-Header in der HTTP-Antwort des Formulars ausgeben:
Content-Disposition: attachment; filename=FILENAME
Mit dem Parameter filename
kann ein Name für die Datei vorgeschlagen werden, in die die Ressource vom Browser heruntergeladen wird. RFC 218 (Content-Disposition) besagt jedoch in Abschnitt 2. (The Filename Parameter), dass der Dateiname nur US-ASCII-Zeichen verwenden kann:
Die aktuelle [RFC 2045] Grammatik beschränkt die Parameterwerte (und damit die Dateinamen für die Inhaltsdisposition) auf US-ASCII. Wir sind uns bewusst, dass es wünschenswert ist, beliebige Zeichensätze in Dateinamen zuzulassen, es würde jedoch den Rahmen dieses Dokuments sprengen, die erforderlichen Mechanismen zu definieren.
Es gibt jedoch empirische Beweise dafür, dass die meisten gängigen Webbrowser heutzutage offenbar Nicht-US-ASCII-Zeichen zulassen, die jedoch (mangels eines Standards) nicht mit dem Kodierungsschema und der Zeichensatzspezifikation des Dateinamens übereinstimmen. Die Frage ist dann, welche verschiedenen Schemata und Codierungen werden von den gängigen Browsern verwendet, wenn der Dateiname "naivefile" (ohne Anführungszeichen und mit dem dritten Buchstaben U + 00EF) in den Content-Disposition-Header codiert werden muss.
Im Sinne dieser Frage sind beliebte Browser :
Dies wird im vorgeschlagenen RFC 5987 "Zeichensatz- und Sprachcodierung für HTTP-Header-Feldparameter (HTTP = Hypertext Transfer Protocol)" diskutiert, einschließlich Links zu Browsertests und zur Abwärtskompatibilität.
RFC 218 gibt an, dass solche Header gemäß RFC 2184 codiert werden sollten, was durch RFC 2231 überholt wurde und durch den obigen RFC-Entwurf abgedeckt wird.
Ich weiß, dass dies ein alter Beitrag ist, aber er ist immer noch sehr relevant. Ich habe herausgefunden, dass moderne Browser rfc5987 unterstützen, was utf-8-Codierung und prozentuale Codierung (url-codiert) ermöglicht. Dann wird Naive file.txt:
Content-Disposition: attachment; filename*=UTF-8''Na%C3%AFve%20file.txt
Safari (5) unterstützt dies nicht. Verwenden Sie stattdessen den Safari-Standard, indem Sie den Dateinamen direkt in Ihren mit utf-8 codierten Header schreiben:
Content-Disposition: attachment; filename=Naïve file.txt
IE8 und ältere Versionen unterstützen dies ebenfalls nicht und Sie müssen den IE-Standard der utf-8-Codierung verwenden, prozentual codiert:
Content-Disposition: attachment; filename=Na%C3%AFve%20file.txt
In ASP.Net verwende ich den folgenden 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);
Ich habe das Obige mit IE7, IE8, IE9, Chrome 13, Opera 11, FF5, Safari 5 getestet.
Update November 2013:
Hier ist der Code, den ich derzeit verwende. Ich muss noch IE8 unterstützen, damit ich den ersten Teil nicht loswerden kann. Es hat sich herausgestellt, dass Browser auf Android den integrierten Android -Download-Manager verwenden und Dateinamen nicht auf die übliche Weise zuverlässig analysieren können.
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);
Das oben Gesagte wurde jetzt in IE7-11, Chrome 32, Opera 12, FF25, Safari 6 mit folgendem Dateinamen zum Herunterladen getestet: 你好 abcABCæøåÆØÅäöüïëêîâéíáóúýñ½§! # ¤% & () = `@ £ $ € {[]} + ´¨ ^ ~ '-_,;. Txt
In IE7 funktioniert es für einige Zeichen, aber nicht für alle. Aber wen interessiert heutzutage der IE7?
Dies ist die Funktion, mit der ich sichere Dateinamen für Android generiere. Beachten Sie, dass ich nicht weiß, welche Zeichen von Android unterstützt werden, aber ich habe getestet, dass diese auf jeden Fall funktionieren:
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: Ich habe in IE7 und IE8 getestet und es stellte sich heraus, dass ich Apostroph nicht entkommen musste ('). Haben Sie ein Beispiel, bei dem es fehlschlägt?
@ Dave Van den Eynde: Das Kombinieren der beiden Dateinamen in einer Zeile gemäß RFC6266 funktioniert mit Ausnahme von Android und IE7 + 8, und ich habe den Code entsprechend aktualisiert. Vielen Dank für den Vorschlag.
@Thilo: Keine Ahnung über GoodReader oder andere Nicht-Browser. Vielleicht haben Sie etwas Glück mit dem Android -Ansatz.
@ Alex Zhukovskiy: Ich weiß nicht warum, aber wie am Connect besprochen, scheint es nicht besonders gut zu funktionieren.
Es gibt keine interoperable Möglichkeit, Nicht-ASCII-Namen in Content-Disposition
zu codieren. Browser-Kompatibilität ist ein Chaos .
Die theoretisch korrekte Syntax für die Verwendung von UTF-8 in Content-Disposition
ist sehr seltsam: filename*=UTF-8''foo%c3%a4
(ja, das ist ein Sternchen, und keine Anführungszeichen außer einem leeren einfachen Anführungszeichen in der Mitte )
Dieser Header ist ein bisschen-nicht-ganz-Standard ( HTTP/1.1-Spezifikation bestätigt seine Existenz , erfordert jedoch keine Clients, um ihn zu unterstützen).
Es gibt eine einfache und sehr robuste Alternative: Verwenden Sie eine URL, die den gewünschten Dateinamen enthält .
Wenn der Name nach dem letzten Schrägstrich der gewünschte ist, benötigen Sie keine zusätzlichen Überschriften!
Dieser Trick funktioniert:
/real_script.php/fake_filename.doc
Wenn Ihr Server das Umschreiben von URLs unterstützt (z. B. mod_rewrite
in Apache), können Sie den Skriptteil vollständig ausblenden.
Zeichen in URLs sollten byteweise in UTF-8 urlenkodiert sein:
/mot%C3%B6rhead # motörhead
RFC 6266 beschreibt die " Verwendung des Content-Disposition-Header-Feldes im Hypertext Transfer Protocol (HTTP) ". Aus dem zitieren:
6. Überlegungen zur Internationalisierung
Mit dem Parameter "
filename*
" ( Abschnitt 4. ) und der in [ RFC5987 ] definierten Codierung kann der Server Zeichen außerhalb von ISO-8859-1 übertragen Zeichensatz und optional zur Angabe der verwendeten Sprache.
Und in ihrem Beispiele Abschnitt :
Dieses Beispiel ist dasselbe wie das oben angegebene, jedoch wird der Parameter "filename" hinzugefügt, um die Kompatibilität mit Benutzeragenten zu gewährleisten, die RFC 5987 nicht implementieren:
Content-Disposition: attachment; filename="EURO rates"; filename*=utf-8''%e2%82%ac%20rates
Hinweis: Benutzerprogramme, die die Codierung RFC 5987 nicht unterstützen, ignorieren "
filename*
", wenn sie nach "filename
" auftreten.
In Anhang D gibt es auch eine lange Liste von Vorschlägen zur Verbesserung der Interoperabilität. Es zeigt auch auf eine Site, die Implementierungen vergleicht . Aktuelle All-Pass-Tests, die für gängige Dateinamen geeignet sind, umfassen:
filename
" verwendet.Das RFC 5987 verweist wiederum auf RFC 2231 , das das tatsächliche Format beschreibt. 2231 ist hauptsächlich für E-Mails bestimmt, und 5987 gibt an, welche Teile auch für HTTP-Header verwendet werden können. Verwechseln Sie dies nicht mit MIME-Headern, die in einem multipart/form-data
HTTP body verwendet werden, der von RFC 2388 ( section 4.4 insbesondere) und der HTML 5-Entwurf .
Das folgende Dokument, das von dem Entwurf des RFC verlinkt ist und von Jim in seiner Antwort erwähnt wird, geht weiter auf die Frage ein und ist auf jeden Fall eine direkte Anmerkung wert:
Testfälle für HTTP Content-Disposition-Header und RFC 2231/2047-Codierung
in asp.net mvc2 benutze ich so etwas:
return File(
tempFile
, "application/octet-stream"
, HttpUtility.UrlPathEncode(fileName)
);
Ich denke, wenn Sie nicht MVC (2) verwenden, können Sie einfach den Dateinamen mit codieren
HttpUtility.UrlPathEncode(fileName)
Setzen Sie den Dateinamen in doppelte Anführungszeichen. Löste das Problem für mich. So was:
Content-Disposition: attachment; filename="My Report.doc"
http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download
Ich habe mehrere Optionen getestet. Browser unterstützen die Spezifikationen nicht und verhalten sich anders. Ich halte doppelte Anführungszeichen für die beste Option.
Ich verwende die folgenden Codeausschnitte zum Codieren (vorausgesetzt fileName enthält den Dateinamen und die Erweiterung der Datei, d. H .: 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 + "\"");
In der ASP.NET-Web-API codiere ich den Dateinamen per 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;
}
}
Wenn Sie ein nodejs-Backend verwenden, können Sie den folgenden Code verwenden, den ich gefunden habe hier
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);
}
Ich habe den folgenden Code in allen gängigen Browsern getestet, einschließlich älterer Explorer (über den Kompatibilitätsmodus), und er funktioniert überall gut:
$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.'"');
Ich habe den folgenden Code in meinem "download.php" -Skript gefunden (basierend auf diesem Blogpost und diesen Testfällen ).
$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) ));
Dies verwendet die Standardmethode von filename = "...", solange nur iso-latin1- und "safe" -Zeichen verwendet werden. Andernfalls wird der Dateiname * = UTF-8 '' URL-codiert hinzugefügt. Entsprechend diesem speziellen Testfall sollte es ab MSIE9 und auf den neuesten Versionen von FF, Chrome, Safari funktionieren. In der niedrigeren MSIE-Version sollte der Dateiname die ISO8859-1-Version des Dateinamens enthalten und Unterstriche für Zeichen enthalten, die nicht in dieser Codierung enthalten sind.
Schlussbemerkung: die max. Größe für jedes Header-Feld ist 8190 Bytes auf Apache. UTF-8 kann bis zu vier Bytes pro Zeichen enthalten. nach rawurlencode sind es x3 = 12 bytes pro zeichen. Ziemlich ineffizient, aber es sollte theoretisch immer noch möglich sein, mehr als 600 "Lächeln"% F0% 9F% 98% 81 im Dateinamen zu haben.
In PHP hat dies für mich funktioniert (vorausgesetzt, der Dateiname ist UTF8-codiert):
header('Content-Disposition: attachment;'
. 'filename="' . addslashes(utf8_decode($filename)) . '";'
. 'filename*=utf-8\'\'' . rawurlencode($filename));
Getestet gegen IE8-11, Firefox und Chrome.
Wenn der Browser Dateiname * = utf-8 interpretieren kann, wird die UTF8-Version des Dateinamens verwendet, andernfalls wird der dekodierte Dateiname verwendet. Wenn Ihr Dateiname Zeichen enthält, die in ISO-8859-1 nicht dargestellt werden können, sollten Sie stattdessen iconv
verwenden.
PHP Framework Symfony 4 hat $filenameFallback
in HeaderUtils::makeDisposition
. Sie können in diese Funktion nach Einzelheiten schauen - sie ähnelt den obigen Antworten.
Anwendungsbeispiel:
$filenameFallback = preg_replace('#^.*\.#', md5($filename) . '.', $filename);
$disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $filename, $filenameFallback);
$response->headers->set('Content-Disposition', $disposition);
Nur ein Update, seit ich all diese Dinge heute als Antwort auf ein Kundenproblem ausprobiert habe
Die meisten modernen Browser unterstützen die Übergabe von Filename
als UTF-8
, aber wie es bei einer von mir verwendeten Datei-Upload-Lösung der Fall war, die auf FreeASPUpload.Net(site nicht mehr vorhanden, Link verweist auf archive.org ) es würde nicht funktionieren, da das Parsen der Binärdatei auf dem Lesen von Zeichenfolgen mit Einzelbyte-ASCII-Codierung beruhte, was gut funktionierte Wenn Sie UTF-8-codierte Daten übergeben haben, bis Sie zu den Zeichen ASCII gelangen, wird dies nicht unterstützt.
Ich konnte jedoch eine Lösung finden, um den Code zum Lesen und Parsen der Binärdatei als UTF-8 zu erhalten.
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
Gutschrift geht an Pure ASP File Upload durch Implementierung der BytesToString()
-Funktion von include_aspuploader.asp
in meinem eigenen Code konnte ich UTF-8
Dateinamen erhalten Arbeiten.