it-swarm-tr.com

HTTP'de Content-Disposition başlığının dosya adı parametresi nasıl kodlanır?

Bir kaynağı zorlamak isteyen Web uygulamaları, bir Web tarayıcısında downloads yerine doğrudan rendered Web tarayıcısında HTTP'de bir Content-Disposition başlığı yayınlar formun cevabı:

Content-Disposition: attachment; filename=FILENAME

filename parametresi, kaynağın tarayıcı tarafından indirildiği dosyanın adını belirtmek için kullanılabilir. RFC 218 (Content-Disposition), ancak bölüm 2. (Dosya Adı Parametresi), dosya adının yalnızca US-ASCII karakterlerini kullanabileceğini belirtir:

Geçerli [RFC 2045] dilbilgisi, parametre değerlerini (ve dolayısıyla İçerik Eğilimindeki dosya adlarını) US-ASCII ile sınırlar. Dosya adlarında rasgele karakter kümelerine izin verilmesinin arzu edildiğini biliyoruz, ancak gerekli mekanizmaları tanımlamak bu belgenin kapsamı dışında.

Yine de, günümüzde popüler Web tarayıcılarının çoğunun ABD-ASCII olmayan karakterlere (standart bir eksiklik için) dosya adının kodlama şeması ve karakter kümesi spesifikasyonuna katılmadığına dair deneysel kanıtlar olduğu görülmüştür. Öyleyse soru, “naïvefile” dosya adının (tırnak işaretleri olmadan ve üçüncü harf U + 00EF olduğunda) Content-Disposition başlığına kodlanması gerektiğinde popüler tarayıcılar tarafından kullanılan çeşitli şemalar ve kodlamalar nelerdir?

Bu sorunun amacı için, popüler tarayıcılar varlık:

  • Firefox
  • Internet Explorer
  • Safari
  • Google Chrome
  • Opera
490
Atif Aziz

Önerilen RFC 5987 , "Köprü Metni Aktarım Protokolü (HTTP) Üstbilgi Alan Parametreleri için Karakter Kümesi ve Dil Kodlama" bölümünde tarayıcı testine ve geriye dönük uyumluluğa bağlantılar da dahil olmak üzere bu konunun tartışması var.

RFC 218 , bu gibi başlıkların yukarıdaki taslak RFC tarafından kapsanan RFC 2231 tarafından eskimiş olan RFC 2184 'e göre kodlanması gerektiğini belirtir.

89
Jim

Bunun eski bir yazı olduğunu biliyorum ama hala çok alakalı. Modern tarayıcıların utf-8 kodlamasına, yüzde kodlamasına (url kodlu) izin veren rfc5987'yi desteklediğini buldum. Sonra Naif dosya.txt olur:

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

Safari (5) bunu desteklemiyor. Bunun yerine, dosya adını doğrudan utf-8 kodlu başlığınıza yazmak için Safari standardını kullanmalısınız:

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

IE8 ve daha eskileri de bunu desteklemiyor ve utf-8 kodlaması IE standardını, kodlanmış yüzdesi kullanmanız gerekiyor:

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

ASP.Net'te aşağıdaki kodu kullanıyorum:

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

Yukarıdakileri IE7, IE8, IE9, Chrome 13, Opera 11, FF5, Safari 5 kullanarak test ettim.

Güncelleme Kasım 2013:

İşte şu anda kullandığım kod. Hala IE8'i desteklemem gerekiyor, bu yüzden ilk bölümden kurtulamıyorum. Android'deki tarayıcıların yerleşik Android indirme yöneticisini kullandığı ve dosya adlarını standart biçimde güvenilir şekilde çözümleyemediği ortaya çıktı.

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

Yukarıdakiler şimdi IE7-11, Chrome 32, Opera 12, FF25, Safari 6'da test edildi ve bu dosya adını indirme amacıyla kullandı: 你好 abcABCæøåÆØÅäöüïëêîâéíáóúýñ½§! ^ ~ '-_;. txt

IE7’de bazı karakterler için çalışır ancak hepsi değildir. Ama bugünlerde IE7'yi kim önemsiyor?

Bu Android için güvenli dosya isimleri oluşturmak için kullandığım fonksiyon. Android'de hangi karakterlerin desteklendiğini bilmediğimi, ancak bunun kesin olarak çalıştığını test ettiğimi unutmayın:

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 ve IE8'de test ettim ve apostrophe (')' den kaçmaya ihtiyacım olmadığı ortaya çıktı. Başarısız olduğu bir yer var mı?

@Dave Van den Eynde: İki dosya adının bir satırda RFC6266'ya göre bir araya getirilmesi Android ve IE7 + 8 hariç çalışır ve kodu yansıtacak şekilde güncelledik. Öneriniz için teşekkür ederim.

@Thilo: GoodReader veya başka bir tarayıcı olmayan hakkında bir fikrim yok. Android yaklaşımını kullanırken biraz şansınız olabilir.

@Alex Zhukovskiy: Nedenini bilmiyorum ama tartışıldığı gibi Connect çok iyi bir şekilde çalışmıyor gibi görünüyor.

340

Basit ve çok sağlam bir alternatif var: istediğiniz dosya adını içeren bir URL kullanın .

Son eğik çizgiden sonraki ad istediğiniz adsa, fazladan başlık kullanmanıza gerek yoktur!

Bu numara işe yarıyor:

/real_script.php/fake_filename.doc

Sunucunuz URL yeniden yazmayı destekliyorsa (örneğin, Apache'de mod_rewrite), komut dosyası bölümünü tamamen gizleyebilirsiniz.

URL’lerdeki karakterler, bayt bayt kodlu UTF-8’de olmalıdır:

/mot%C3%B6rhead   # motörhead
161
Kornel

RFC 6266 , “Köprü Metni Aktarım Protokolü'ndeki (HTTP)” içindeki İçerik-Yerleşim Başlık Alanının Kullanılmasını açıklar. Bundan alıntı yapmak:

6. Uluslararasılaşma ile İlgili Hususlar

filename*” parametresi ( Bölüm 4. ), [ RFC5987 ] içinde tanımlanan kodlamayı kullanarak, sunucunun ISO-8859-1 karakter kümesi dışındaki karakterleri ve ayrıca isteğe bağlı olarak kullanılan dili belirtin.

Ve onların örnekler bölümünde :

Bu örnek yukarıdaki ile aynıdır, ancak RFC 5987 uygulamamış kullanıcı aracılarıyla uyumluluk için "dosya adı" parametresini ekleyin:

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

Not: RFC 5987 kodlamasını desteklemeyen kullanıcı aracıları, “filename” ifadesinden sonra gerçekleştiğinde “filename*” öğesini yok sayar.

Ek D 'de birlikte çalışabilirliği artırmak için uzun bir öneri listesi de bulunmaktadır. Ayrıca, ygulamaları karşılaştıran bir site işaretini de gösterir. Yaygın dosya adlarına uygun güncel all-pass testleri şunları içerir:

  • attwithisofnplain : çift tırnaklı ve kodlamayan düz ISO-8859-1 dosya adı. Bu, hepsi ISO-8859-1 olan ve en az altıgen basamağın önünde olmayan, yüzde işareti içermeyen bir dosya adı gerektirir.
  • attfnboth : yukarıda açıklanan sıraya göre iki parametre. IE8 “filename” parametresini kullanmasına rağmen, çoğu tarayıcıdaki çoğu dosya adı için çalışmalıdır.

Bu, RFC 5987 sırayla, asıl formatı tanımlayan RFC 2231 ifadesine atıfta bulunur. 2231 öncelikli olarak posta içindir ve 5987 bize HTTP başlıkları için hangi parçaların kullanılabileceğini söyler. Bunu, RFC 2388 ( özellikle bölüm 4.4 tarafından yönetilen) ve ({özellikle RFC 2388)} _ tarafından yönetilen bir multipart/form-data HTTPbodyiçinde kullanılan MIME başlıklarıyla karıştırmayın. HTML 5 taslağı .

64
MvG

RFC taslağı ile belirtilen Jim tarafından verilen cevabında yer alan aşağıdaki belge, soruyu daha da ileriye götürür ve kesinlikle burada doğrudan bir nota değecektir:

HTTP Content-Disposition başlığı ve RFC 2231/2047 Kodlaması için Test Durumları

16
Atif Aziz

aSP.NET mvc2 içinde böyle bir şey kullanın:

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

Sanırım mvc (2) kullanmazsanız dosya adını kullanarak kodlayabilirsiniz.

HttpUtility.UrlPathEncode(fileName)
11
Elmer

Dosya adını çift tırnak içine alın. Sorunu benim için çözdü. Bunun gibi:

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

http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download

Birden çok seçeneği test ettim. Tarayıcılar özellikleri desteklemiyor ve farklı davranıyor, çift tırnak en iyi seçenek olduğuna inanıyorum.

10

Kodlama için aşağıdaki kod parçacıklarını kullanıyorum (varsayarak dosyaAdı, dosyanın dosya adını ve uzantısını içerir, örneğin: 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 + "\"");
9

ASP.NET Web API'sinde, dosya adını kodlarım:

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 Not fixed
IE 9 Fixed

8
martinoss

Eğer bir nodejs arka uç kullanıyorsanız, bulduğum aşağıdaki kodu kullanabilirsiniz burada

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
Emanuele Spatola

Eski Kodlayıcılar dahil tüm büyük tarayıcılarda (uyumluluk modu aracılığıyla) aşağıdaki kodu test ettim ve her yerde iyi çalışıyor:

$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
Stano

"Download.php" betiğimde şu kodu kullandım ( bu blogpost ve bu test durumları ).

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

Bu, yalnızca iso-latin1 ve "safe" karakterleri kullanıldığı sürece standart dosya adı = "..." kullanır; değilse * dosya adını ekler * = UTF-8 '' url kodlu yolu. bu özel test vakası 'ya göre, MSIE9 ve sonraki FF, Chrome, Safari’de çalışmalıdır; alt MSIE sürümünde, bu kodlamanın içinde olmayan karakterlerin altını çizerek, dosya adının ISO8859-1 sürümünü içeren dosya adını sunmalıdır.

Son not: maks. Her başlık alanının boyutu Apache’de 8190 bayttır. UTF-8 karakter başına dört bayta kadar olabilir; rawurlencode'dan sonra, her karakter için x3 = 12 bayttır. Oldukça verimsiz, ancak dosya adında 600'den fazla "gülümsemeye"% F0% 9F% 98% 81 sahip olmak hala teorik olarak mümkün olmalıdır.

4
renergy

PHP içinde bu benim için yaptı (dosya adının UTF8 kodlu olduğunu varsayarsak):

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

IE8-11, Firefox ve Chrome'a ​​karşı test edilmiştir.
Tarayıcı, yorumlayabiliyorsa dosya adı * = utf-8, dosya adının UTF8 sürümünü kullanır, aksi halde kodu çözülmüş dosya adını kullanır. Dosya adınız ISO-8859-1 ile temsil edilemeyen karakterler içeriyorsa, bunun yerine iconv öğesini kullanmayı düşünebilirsiniz.

3
Gustav

PHP çerçevesi Symfony 4, $filenameFallback içinde HeaderUtils::makeDisposition değerine sahiptir. Ayrıntılar için bu işleve bakabilirsiniz - yukarıdaki cevaplara benzer.

Kullanım örneği:

$filenameFallback = preg_replace('#^.*\.#', md5($filename) . '.', $filename);
$disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $filename, $filenameFallback);
$response->headers->set('Content-Disposition', $disposition);
1
luchaninov

Bugün bir müşteri sorununa cevaben bütün bunları denediğimden beri sadece bir güncelleme

  • Japonca için yapılandırılmış Safari dışında, test ettiğimiz tüm tarayıcılar en iyi dosya adı = text.pdf ile çalıştı - burada metin, ASP.Net/IIS tarafından utl-8'de url kodlaması olmadan seri hale getirilmiş bir müşteri değeridir. Herhangi bir nedenle, İngilizce için yapılandırılmış Safari, utf-8 Japonca adındaki bir dosyayı kabul eder ve düzgün bir şekilde kaydeder, ancak Japonca için yapılandırılan aynı tarayıcı, dosyayı utf-8 karakterleri yorumlanmamış olarak kaydeder. Test edilen diğer tüm tarayıcılar, url kodlaması olmadan utf-8 dosya adıyla (dil yapılandırmasından bağımsız olarak) en iyi/iyi çalışıyor gibi görünüyordu.
  • Rfc5987/8187 hiç uygulayan tek bir tarayıcı bulamadım. En son Chrome ile test ettim, Firefox oluşturur artı IE 11 ve Edge. Üstbilgiyi sadece dosya adıyla ayarlamaya çalıştım * = utf-8''texturlencoded.pdf, her iki dosya adıyla da ayarlayarak = text.pdf; Dosya adı * = utf-8''texturlencoded.pdf. Rfc5987/8187'nin bir özelliğinin yukarıdakilerin hiçbirinde doğru şekilde işlenmediği görülmedi.
1
user1664043

Klasik ASP Çözüm

Çoğu modern tarayıcı, Filename'yi şimdi UTF-8 olarak geçirmeyi destekliyor, ancak kullandığım bir Dosya Yükleme çözümünde olduğu gibi kullanıyorum FreeASPUpload.Net (site artık mevcut değil, archive.org ) bağlantısına işaret ediyor, ikilinin ayrıştırılması tek bayt okumaya dayanıyordu ASCII kodlu dizeler, UTF-8 kodlu verileri geçtiğinizde, karakterlere ulaşana kadar iyi çalıştı ASCII desteklemiyor.

Ancak kodun ikiliyi UTF-8 olarak okuyup ayrıştırması için bir çözüm bulabildim.

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

Credit 'e gider Saf ASP Dosya YüklemeBytesToString() işlevini kendi kodumda include_aspuploader.asp işlevinden uygulayarak UTF-8 dosya isimlerinin çalışmasını sağlayabildim.


Kullanışlı bağlantılar

1
Lankymart