Jak zakodować parametr filename nagłówka Content-Disposition w HTTP?

Aplikacje internetowe, które chcą wymusić pobranie zasobu zamiast bezpośrednio wyrenderować w przeglądarce internetowej wydanie nagłówka Content-Disposition W odpowiedzi HTTP formularza:

Content-Disposition: attachment; filename=FILENAME

Parametr filename może być użyty do zaproponowania nazwy pliku, do którego zasób jest pobierany przez przeglądarkę. RFC 2183 (Content-Disposition) stwierdza jednak w sekcja 2.3 (parametr Filename), że nazwa pliku może używać tylko US-ASCII znaki:

Current [RFC 2045] wartości parametrów (i stąd Content-Disposition filenames) do US-ASCII. Uznajemy wielkie celowość pozwalająca na dowolne zestawów znaków w nazwach plików, ale jest poza zakresem niniejszego dokumentu do zdefiniuj niezbędne mechanizmy.

Istnieją jednak dowody empiryczne, że większość popularnych przeglądarek internetowych wydaje się zezwalać na znaki spoza US-ASCII (z powodu braku standardu) nie zgadzają się co do schematu kodowania i specyfikacji zestawu znaków nazwy pliku. Pytanie brzmi zatem, jakie są różne schematy i kodowania stosowane przez popularne przeglądarki, jeśli nazwa pliku "naïvefile" (bez cudzysłowów i gdzie trzecia litera to U+00EF) musi być zakodowana w nagłówku Content-Disposition?

Na potrzeby tego pytania, popularne przeglądarki to:

  • Firefox
  • Internet Explorer
  • Safari
  • Google Chrome
  • Opera
Author: bignose, 2008-09-18

17 answers

W proponowanym RFC 5987, "Zestaw znaków i kodowanie języka dla parametrów pola nagłówka protokołu HTTP (Hypertext Transfer Protocol) omówiono ten temat, w tym linki do testowania przeglądarki i kompatybilności wstecznej."

RFC 2183 wskazuje, że takie nagłówki powinny być zakodowane zgodnie z RFC 2184 , które zostało przestarzałe przez RFC 2231, objęte szkicem RFC powyżej.

 81
Author: Jim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-25 07:10:59

Wiem, że to stary post, ale nadal jest bardzo istotny. Odkryłem, że nowoczesne przeglądarki obsługują rfc5987, który umożliwia kodowanie utf-8, kodowanie procentowe (kodowanie url). / Align = "left" / txt staje się:

Content-Disposition: attachment; filename*=UTF-8''Na%C3%AFve%20file.txt

Safari (5) tego nie obsługuje. Zamiast tego powinieneś użyć standardu Safari zapisu nazwy pliku bezpośrednio w zakodowanym nagłówku utf-8:

Content-Disposition: attachment; filename=Naïve file.txt

IE8 i starsze też go nie obsługują i trzeba użyć standardu IE kodowania utf-8, procent

Content-Disposition: attachment; filename=Na%C3%AFve%20file.txt

W ASP.Net używam następującego kodu:

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);

Testowałem powyższe używając IE7, IE8, IE9, Chrome 13, Opera 11, FF5, Safari 5.

Aktualizacja Listopad 2013:

Oto kod, którego obecnie używam. Nadal muszę wspierać IE8, więc nie mogę pozbyć się pierwszej części. Okazuje się, że przeglądarki na Androidzie używają wbudowanego menedżera pobierania Androida i nie mogą niezawodnie analizować nazw plików w standardowy sposób.

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);

Powyższe teraz Przetestowane w IE7-11, Chrome 32, Opera 12, FF25, Safari 6, używanie tej nazwy pliku do pobrania: #¤%&()=`@£$€{[]}+´¨^~'-_,;.txt

Na IE7 działa dla niektórych znaków, ale nie dla wszystkich. Ale kogo teraz obchodzi IE7?

Jest to funkcja, której używam do generowania bezpiecznych nazw plików dla Androida. Zauważ, że nie wiem, które znaki są obsługiwane na Androidzie, ale przetestowałem, że te działają na pewno:

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: testowałem w IE7 i IE8 i okazało się że nie muszę uciekać przed apostrofem ('). Masz jakiś przykład, gdzie to zawodzi?

@Dave Van den Eynde: łączenie dwóch nazw plików w jednej linii, zgodnie z RFC6266 działa z wyjątkiem Androida i IE7+8 i zaktualizowałem kod, aby odzwierciedlić to. Dziękuję za sugestię.

@Thilo: nie mam pojęcia o Goodreaderze ani o innych nie przeglądarkach. Możesz mieć trochę szczęścia przy użyciu podejścia Android.

@ Alex Żukovskiy: Nie wiem dlaczego, ale jak dyskutowano na Połącz to nie działa zbyt dobrze.

 314
Author: Martin Ørding-Thomsen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-13 19:59:07
  • Nie ma interoperacyjnego sposobu kodowania nazw innych niż ASCII w Content-Disposition. zgodność przeglądarki jest bałagan .

  • Teoretycznie poprawna składnia dla użycia UTF - 8 w Content-Disposition jest bardzo dziwna: filename*=UTF-8''foo%c3%a4 (tak, to gwiazdka, bez cudzysłowów poza pustym pojedynczym cytatem w środku)

  • Ten nagłówek jest trochę-nie-całkiem-standardowy (HTTP/1.1 spec , ale nie wymaga od klientów obsługi it).

Istnieje prosta i bardzo solidna alternatywa: użyj adresu URL, który zawiera żądaną nazwę pliku.

Gdy nazwa po ostatnim ukośniku jest tą, którą chcesz, nie potrzebujesz żadnych dodatkowych nagłówków!

Ta sztuczka działa:

/real_script.php/fake_filename.doc

I jeśli twój serwer obsługuje przepisywanie adresów URL (np. mod_rewrite W Apache), możesz całkowicie ukryć część skryptu.

Znaki w adresach URL powinny być w UTF-8, urlencoded byte-by-byte:

/mot%C3%B6rhead   # motörhead
 150
Author: Kornel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-06 15:42:50

RFC 6266 opisuje " użycie pola nagłówka Content-Disposition w Hypertext Transfer Protocol (HTTP) ". Cytuję z tego:

6. Względy Internacjonalizacji

Parametr "filename*" (sekcja 4.3 ), przy użyciu zdefiniowanego kodowania w [RFC5987 ], pozwala serwerowi na przesyłanie znaków poza ISO-8859-1 zestaw znaków, a także opcjonalnie określić język w użyj.

I w sekcji przykłady :

Ten przykład jest taki sam jak powyższy, ale dodaje " filename" parametr zgodności z agentami użytkownika nie implementującymi RFC 5987:

Content-Disposition: attachment;
                     filename="EURO rates";
                     filename*=utf-8''%e2%82%ac%20rates

Uwaga: użytkownicy, którzy nie obsługują kodowania RFC 5987 ignoruj "filename*", gdy występuje po "filename".

W dodatku D znajduje się również długa lista propozycji zwiększenia interoperacyjność. Wskazuje również na stronę, która porównuje implementacje. Aktualne testy all-pass odpowiednie dla wspólnych nazw plików to:

  • attwithisofnplain : zwykła nazwa pliku ISO-8859-1 z podwójnymi cudzysłowami i bez kodowania. Wymaga to nazwy pliku, która jest zgodna z normą ISO-8859 - 1 i nie zawiera znaków procentowych, przynajmniej nie przed cyframi szesnastkowymi.
  • attfnboth: dwa parametry w kolejności opisanej powyżej. Powinno działać dla większości plików nazwy w większości przeglądarek, chociaż IE8 będzie używał parametru " filename".

To RFC 5987 z kolei odwołuje się do RFC 2231 , który opisuje rzeczywisty format. 2231 jest przede wszystkim dla poczty, a 5987 mówi nam, jakie części mogą być używane również dla nagłówków HTTP. Nie należy mylić tego z nagłówkami MIME używanymi wewnątrz multipart/form-data http ciała, które jest regulowane przez RFC 2388 (sekcja 4.4 w szczególności) I HTML 5 draft.

 58
Author: MvG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-05 12:54:48

Poniższy dokument powiązany z szkic RFC wspomniany przez Jim w jego odpowiedzi dalej odnosi się do pytania i zdecydowanie wart bezpośredniej uwagi tutaj:

Przypadki testowe dla nagłówka HTTP Content-Disposition i kodowania RFC 2231/2047

 16
Author: Atif Aziz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:28

W asp.net mvc2 używam czegoś takiego:

return File(
    tempFile
    , "application/octet-stream"
    , HttpUtility.UrlPathEncode(fileName)
    );

Myślę, że jeśli nie używasz mvc(2), możesz po prostu zakodować nazwę pliku używając

HttpUtility.UrlPathEncode(fileName)
 11
Author: Elmer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-12 20:03:02

Do Kodowania używam następujących fragmentów kodu (zakładając, że fileName zawiera nazwę pliku i rozszerzenie pliku, np.: 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
Author: Vassilen Dontchev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-06 15:45:59

W ASP.NET Web API, i Url koduje nazwę pliku:

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;
    }
}

IE 9 nie Naprawiono
IE 9 Fixed

 8
Author: martinoss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-25 08:10:39

Umieść nazwę pliku w podwójnych cudzysłowach. Rozwiązałem problem. Tak:

Content-Disposition: attachment; filename="My Report.doc"

Http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download

 8
Author: Dmitry Kaigorodov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-10 15:01:51

Testowałem następujący kod we wszystkich głównych przeglądarkach, w tym starszych Explorerach( poprzez tryb zgodności), i działa dobrze wszędzie:

$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.'"');
 5
Author: Stano,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-21 21:08:26

Jeśli używasz zaplecza nodejs, możesz użyć następującego kodu, który znalazłem tutaj

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);
}
 5
Author: Emanuele Spatola,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-27 08:36:28

Skończyłem z poniższym kodem w moim " download.skrypt php " (oparty na Ten blogpost i te przypadki testowe ).

$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) ));

Używa standardowej metody filename="..."tak długo, jak są używane tylko znaki iso-latin1 i "bezpieczne"; jeśli nie, dodaje nazwę pliku * = UTF-8" kodowany url sposób. Zgodnie z ten konkretny przypadek testowy , powinien działać od MSIE9 w górę, a na ostatnich FF, Chrome, Safari; w niższej wersji MSIE powinien oferować nazwę pliku zawierającą ISO8859-1 wersja nazwy pliku, z podkreślnikami na znakach Nie w tym kodowaniu.

Ostatnia uwaga: max. rozmiar każdego pola nagłówka wynosi 8190 bajtów na apache. UTF - 8 może mieć maksymalnie cztery bajty na znak; po rawurlencode jest to x3 = 12 bajtów na jeden znak. Dość nieefektywne, ale teoretycznie powinno być możliwe posiadanie ponad 600 "uśmiechów" %F0%9F%98%81 w nazwie pliku.

 4
Author: renergy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-05 15:45:29

W PHP zrobiłem to za mnie (zakładając, że nazwa pliku jest zakodowana w UTF8):

header('Content-Disposition: attachment;'
    . 'filename="' . addslashes(utf8_decode($filename)) . '";'
    . 'filename*=utf-8\'\'' . rawurlencode($filename));

Testowane na IE8-11, Firefox i Chrome.
Jeśli przeglądarka potrafi zinterpretować filename * = utf-8 użyje wersji UTF8 nazwy pliku, w przeciwnym razie użyje zdekodowanej nazwy pliku. Jeśli nazwa pliku zawiera znaki, których nie można przedstawić w ISO-8859-1, warto rozważyć użycie iconv.

 3
Author: Gustav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-28 10:58:10

Klasyczne rozwiązanie ASP

Większość nowoczesnych przeglądarek obsługuje przekazywanie Filename jako UTF-8 teraz, ale tak jak w przypadku rozwiązania przesyłania plików używam, które było oparte na FreeASPUpload.Net ( Strona już nie istnieje, link wskazuje na archive.org) to nie działa, ponieważ parsowanie binarne polegało na odczytywaniu pojedynczych bajtów ASCII zakodowanych ciągów, co działało dobrze, gdy przekazywałeś zakodowane dane UTF-8, dopóki nie dotarłeś do znaków ASCII nie obsługuje.

Jednakże Udało mi się znaleźć rozwiązanie, aby kod odczytać i przetworzyć kod binarny jako 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

Kredyt idzie do Pure ASP File Upload implementując BytesToString() funkcję z include_aspuploader.asp w moim własnym kodzie udało mi się uruchomić UTF-8 nazwy plików.


Przydatne Linki

 1
Author: Lankymart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:23

Mieliśmy podobny problem w aplikacji internetowej i skończyło się na odczytaniu nazwy pliku z HTML <input type="file"> i ustawieniu jej w postaci zakodowanej url w nowym HTML <input type="hidden">. Oczywiście musieliśmy usunąć ścieżkę jak "C:\fakepath\", który jest zwracany przez niektóre przeglądarki.

Oczywiście nie odpowiada to bezpośrednio na pytanie OPs, ale może być rozwiązaniem dla innych.

 -1
Author: Andrei I,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-27 11:54:13

Normalnie KODUJĘ URL (z %xx) nazwy plików i wydaje się, że działa we wszystkich przeglądarkach. I tak powinieneś zrobić kilka testów.

 -2
Author: Dario Solera,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 15:28:29

Znalazłem rozwiązanie, które działa dla wszystkich moich przeglądarek (np. wszystkie zainstalowane przeze mnie przeglądarki - IE8, FF16, Opera 12, Chrome 22).

Moje rozwiązanie jest opisane w innym wątku: Java servlet pobierz plik znaki specjalne

Moje rozwiązanie opiera się na tym, jak przeglądarki próbują odczytać wartość z parametru filename. Jeśli w parametrze filename Nie podano zestawu znaków (na przykład filename*=utf-8''test.xml) przeglądarki oczekują, że wartość jest zakodowana w natywnym kodowaniu przeglądarki.

Różne przeglądarki oczekują różnych natywnych kodowań. Zazwyczaj natywne kodowanie przeglądarki to utf-8 (FireFox, Opera, Chrome). Ale natywnym kodowaniem IE jest Win-1250. (Nie wiem nic o innych przeglądarkach.)

Stąd, jeśli umieścimy wartość w parametrze filename, który jest kodowany przez utf-8 / win-1250 zgodnie z przeglądarką użytkownika, powinien działać. Przynajmniej mi to pasuje.

W skrócie, jeśli mamy plik o nazwie omáčka.xml,
dla Firefoksa, Opery i Chrome odpowiadam na ten nagłówek (zakodowany w utf-8):

Content-Disposition: attachment; filename="omáčka.xml"

A dla IE odpowiadam ten nagłówek (zakodowany w win-1250):

Content-Disposition: attachment; filename="omáèka.jpg"

Przykładem Javy jest w moim poście, który jest wspomniany powyżej.

 -3
Author: sporak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:06