Как закодировать параметр filename заголовка Content-Disposition в HTTP?
веб-приложения, которые хотят заставить ресурс быть загрузить, а не rendered в веб-браузере проблема a Content-Disposition
заголовок в HTTP-ответ вида:
Content-Disposition: attachment; filename=FILENAME
The может использоваться для указания имени файла, в который браузер загружает ресурс. RFC 2183 (Content-Disposition), однако, состояния в 2.3 (Имя Параметр) что имя файла может использовать только символы US-ASCII:
текущая [RFC 2045] грамматика ограничивает значения параметров (и, следовательно, Назначение содержимого файлов) в США-ASCII. Мы признаем великое желательность разрешения произвольного наборы символов в именах файлов, но это за рамками настоящего документа: определите необходимые механизмы.
есть эмпирические данные, тем не менее, что большинство популярных веб-браузеров сегодня, кажется, разрешить символы, отличные от US-ASCII, но все же (из-за отсутствия стандарта) не согласны со схемой кодирования и спецификацией набора символов имени файла. Вопрос в том, какие различные схемы и кодировки используются популярными браузерами, если имя файла "naïvefile" (без кавычек и где третья буква U+00EF) необходимо закодировать в заголовок Content-Disposition?
для целей этого вопроса,популярные браузеры существо:
- Firefox
- Internet Explorer
- сафари
- Google Chrome
- Опера
17 ответов:
есть обсуждение этого, в том числе ссылки на тестирование браузера и обратную совместимость, в предложенном RFC 5987, " набор символов и кодировка языка для параметров поля заголовка протокола передачи гипертекста (HTTP)."
RFC 2183 указывает на то, что такие заголовки должны быть закодированы в соответствии с RFC 2184, который был заменен на RFC 2231, охватывается проектом RFC выше.
Я знаю, это старый пост, но он по-прежнему очень актуальна. Я обнаружил, что современные браузеры поддерживают rfc5987, который позволяет кодировать utf-8, закодированный в процентах (url-кодированный). Тогда наивный файл.txt становится:
Content-Disposition: attachment; filename*=UTF-8''Na%C3%AFve%20file.txt
Safari (5) не поддерживает это. Вместо этого вы должны использовать стандарт Safari для записи имени файла непосредственно в заголовке с кодировкой utf-8:
Content-Disposition: attachment; filename=Naïve file.txt
IE8 и старше тоже не поддерживают его, и вам нужно использовать стандарт IE кодировки utf-8, процент закодирован:
Content-Disposition: attachment; filename=Na%C3%AFve%20file.txt
In 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 года:
вот код, который я сейчас использую. Я все еще должен поддерживать 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, используя это имя файла для загрузки: 你好abcabcæøåæøåäüïëêîáéíáúýñ½§!#¤%&()=`@£$€{[]}+^~'-_,;.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: объединение двух имен файлов в одной строке, как в соответствии с RFC6266 работает за исключением Android и IE7+8, и я обновил код, чтобы отразить это. Спасибо за предложение.
@Thilo: без понятия о GoodReader или любой другой не-браузер. Возможно, Вам повезет с помощью Android подход.
@Alex Zhukovskiy: я не знаю, почему, но как обсуждалось на подключиться это, кажется, не работает очень хорошо.
не существует совместимого способа кодирования имен, отличных от ASCII, в
Content-Disposition
. совместимость браузера-это беспорядок.The теоретически правильный синтаксис для использования UTF-8 в
Content-Disposition
очень странно:filename*=UTF-8''foo%c3%a4
(да, это звездочка, и никаких кавычек, кроме пустой одинарной кавычки в середине)этот заголовок своего рода-не совсем стандартный (спецификация HTTP/1.1 подтверждает свое существование, но не требует, чтобы клиенты поддерживали его).
существует простая и очень надежная альтернатива:используйте URL-адрес, содержащий имя файла, которое вы хотите.
когда имя после последней косой черты является тот, который вы хотите, вам не нужно никаких дополнительных заголовков!
этот трюк работает:
/real_script.php/fake_filename.doc
и если ваш сервер поддерживает перезапись URL (например
mod_rewrite
в Apache), то вы можете полностью скрыть скрипт часть.символы в URL-адресах должны быть в UTF-8, urlencoded байт за байтом:
/mot%C3%B6rhead # motörhead
RFC 6266 описывает "использование поля заголовка Content-Disposition в протоколе передачи гипертекста (HTTP)". Цитируя из этого:
6. Интернационализация Соображения
"
filename*
" параметр (4.3), используя определенную кодировку в [ RFC5987], позволяет серверу передавать символы вне ISO-8859 - 1 набор символов, а также дополнительно укажите язык в употреблении.и в примеры:
этот пример такой же, как и выше, но добавление " filename" параметр для совместимости с агентами пользователей, не реализующими RFC 5987:
Content-Disposition: attachment; filename="EURO rates"; filename*=utf-8''%e2%82%ac%20rates
Примечание: те агенты пользователей, которые не поддерживают RFC 5987 кодировка игнорировать "
filename*
" когда это происходит после "filename
".на Приложение D существует также длинный список предложений по повышению совместимости. Он также указывает на сайт, который сравнивает реализаций. Текущие тесты all-pass, подходящие для общих имен файлов, включают:
- attwithisofnplain: простое имя файла ISO-8859-1 с двойными кавычками и без кодировки. Для этого требуется имя файла, которое является всем ISO-8859-1 и не содержит знаков процента, по крайней мере, не перед шестнадцатеричными цифрами.
- attfnboth: два параметра в порядке, описанном выше. Должно работать для большинства имен файлов в большинстве браузеров, хотя IE8 будет использовать "
filename
параметр".это RFC 5987 в свою очередь, ссылается RFC 2231, который описывает фактический формат. 2231 в основном для почты, и 5987 говорит нам, какие части могут быть использованы для заголовков HTTP, а также. Не путайте это с заголовками MIME используется внутри
multipart/form-data
HTTP тело, который регулируется RFC 2388 (4.4 в частности) и HTML 5 проект.
следующий документ связан с проект RFC упомянутые Джим в своем ответе далее обращается к вопросу и, безусловно, стоит прямо отметить здесь:
тестовые случаи для заголовка HTTP Content-Disposition и кодировки RFC 2231/2047
в asp.net mvc2 я использую что-то вроде этого:
return File( tempFile , "application/octet-stream" , HttpUtility.UrlPathEncode(fileName) );
Я думаю, если вы не используете mvc(2), Вы можете просто закодировать имя файла с помощью
HttpUtility.UrlPathEncode(fileName)
Я использую следующие фрагменты кода для кодирования (предположим 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 + "\"");
In ASP.NET Web API, я 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; } }
поместите имя файла в двойные кавычки. Решил проблему для меня. Вот так:
Content-Disposition: attachment; filename="My Report.doc"
http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download
я протестировал следующий код во всех основных браузерах, включая старые полярники (через режим совместимости), и он хорошо работает везде:
$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.'"');
Если вы используете серверную часть 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); }
Я закончил со следующим кодом в моей " скачать.на 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) ));
Это использует стандартный способ filename="..."пока используются только символы iso-latin1 и "safe"; если нет, он добавляет имя файла*=UTF-8" url-кодированный способ. Согласно этот конкретный тест, он должен работать от MSIE9 вверх, и на недавнем FF, Chrome, Safari; на более низкой версии MSIE, он должен предложите имя файла, содержащее версию ISO8859-1 имени файла, с подчеркиванием на символах не в этой кодировке.
заключительное Примечание: макс. размер для каждого поля заголовка составляет 8190 байт на Apache. UTF-8 может быть до четырех байт на символ; после rawurlencode это x3 = 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
вместо.
классическое решение ASP
большинство современных браузеров поддерживают передачу
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
кредит идет на Pure ASP File Upload реализация С
include_aspuploader.asp
в моем собственном коде я смог получитьUTF-8
имена рабочих.
Полезные Ссылки
у нас была аналогичная проблема в веб-приложении, и в конечном итоге, прочитав имя файла из HTML
<input type="file">
, и установка этого в url-кодированной форме в новом HTML<input type="hidden">
. Конечно, мы должны были удалить путь, как "C:\fakepath\" это возвращается некоторыми браузерами.конечно, это не дает прямого ответа на вопрос OPs, но может быть решением для других.
Я обычно URL-кодирую (с %xx) имена файлов, и это, кажется, работает во всех браузерах. Вы можете сделать некоторые тесты в любом случае.
Я нашел решение, которое работает для всех моих браузеров (IE. все браузеры, которые я установил-IE8, FF16, Opera 12, Chrome 22).
мое решение описано в другом потоке: Java сервлет скачать имя файла специальные символы
мое решение основано на том, как браузеры пытаются прочитать значение из