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:
Ö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.
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.
ASCII olmayan adları Content-Disposition
içinde kodlamanın birlikte çalışabilir bir yolu yoktur. Tarayıcı uyumluluğu bir karışıklıktır .
teorik olarak doğru sözdizimiContent-Disposition
içinde UTF-8 kullanımı için çok garip: filename*=UTF-8''foo%c3%a4
(evet, bu bir yıldız işaretidir ve ortada boş bir tek alıntı dışında alıntı yok)
Bu başlık oldukça standart değil ( HTTP/1.1 teknik özelliği varlığını onaylar , ancak müşterilerin desteklemesini gerektirmez).
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
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:
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ğı .
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ı
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)
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.
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 + "\"");
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;
}
}
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);
}
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.'"');
"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.
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.
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);
Bugün bir müşteri sorununa cevaben bütün bunları denediğimden beri sadece bir güncelleme
Ç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.