Как закодировать параметр имени файла заголовка Content-Disposition в HTTP?

Веб-приложения, которые хотят заставить ресурс быть скачано, а не напрямую оказано в веб-браузере, выдают заголовок Content-Disposition в HTTP-ответе в форме:

Content-Disposition: attachment; filename=FILENAME

Параметр filename может использоваться для предложения имени файла, в который браузер загружает ресурс. Однако RFC 2183 (Content-Disposition) указывает в Раздел 2.3 (Параметр имени файла), что имя файла может использовать только символы US-ASCII:

Current [RFC 2045] grammar restricts parameter values (and hence Content-Disposition filenames) to US-ASCII. We recognize the great desirability of allowing arbitrary character sets in filenames, but it is beyond the scope of this document to define the necessary mechanisms.

Тем не менее, есть эмпирические доказательства того, что большинство популярных веб-браузеров сегодня допускают символы, отличные от US-ASCII, но (из-за отсутствия стандарта) расходятся во мнениях относительно схемы кодирования и спецификации набора символов имени файла. Тогда возникает вопрос, какие различные схемы и кодировки используются в популярных браузерах, если имя файла «naïvefile» (без кавычек и третья буква U + 00EF) необходимо закодировать в заголовке Content-Disposition?

Для целей этого вопроса популярные браузеры:

  • Fire Fox
  • Internet Explorer
  • Сафари
  • Гугл Хром
  • Опера

Он работает для Mobile Safari (необработанный utf-8, как было предложено @Martin Ørding-Thomsen), но это не работает для GoodReader с того же устройства. Есть идеи?

Thilo 08.03.2012 12:14

Также см. этот похожий вопрос

juergen d 30.08.2016 17:39
Корнель ответ proved to be the path of least resistance, if you can set the last segment of the path; couple this with Content-Disposition: attachment.
Antti Haapala 12.09.2016 00:34
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
558
3
378 366
18
Перейти к ответу Данный вопрос помечен как решенный

Ответы 18

Я обычно URL-кодирую (с% xx) имена файлов, и, похоже, это работает во всех браузерах. В любом случае вы можете захотеть провести несколько тестов.

Я тестировал несколько, и это не работает во всех браузерах, поэтому вопрос. :)

Atif Aziz 18.09.2008 19:31
Ответ принят как подходящий

Это обсуждается, включая ссылки на тестирование браузера и обратную совместимость, в предложенном RFC 5987, «Набор символов и языковая кодировка для параметров поля заголовка протокола передачи гипертекста (HTTP)».

RFC 2183 указывает, что такие заголовки должны быть закодированы в соответствии с RFC 2184, который был отменен RFC 2231, охваченным проектом RFC выше.

С быстрым тестом, который реализован Firefox и ужасно сломан в IE: он просто не распознает «filename *» как имя файла и пытается определить имя файла из mime-типа и последней части URL-адреса.

lapo 16.02.2011 19:22

Это было частично исправлено в IE9.

Julian Reschke 09.09.2011 19:28

Также обратите внимание, что интернет-черновик (не «черновик RFC») был завершен, а окончательный документ - RFC 5987 (greenbytes.de/tech/webdav/rfc5987.html).

Julian Reschke 29.09.2011 19:46

В связи с этим я обнаружил, что Firefox (версии 4-9 включительно) прерывается, если в имени файла есть запятая (,), например Content-Disposition: filename = "foo, bar.pdf". В результате firefox правильно загружает файл, но сохраняет расширение .part (например, foo,bar.pdf-1.part). Тогда, конечно, файл не откроется правильно, потому что приложение не связано с .part. Другие символы ASCII, похоже, работают нормально.

catchdave 11.01.2012 04:09

RFC, как хорошо известно, «неправильно реализован» различными браузерами. IE, Chrome, Fx, особенно стандартный браузер Android, очень сильно отличаются в кодировке Unicode / non-ascii.

Dennis C 13.09.2013 06:16

@DennisCheung У вас есть ссылка на эти несоответствия в работе браузера? (Я хочу предоставить доказательства проблемы).

Matthew Schinckel 08.10.2013 03:03

@MatthewSchinckel например kbyanc.blogspot.hk/2010/07/… и digiblog.de/2011/04/android-and-the-download-file-headers

Dennis C 08.10.2013 05:19

Для получения дополнительной информации о поведении IE см. blogs.msdn.com/b/ieinternals/archive/2010/06/07/….

EricLaw 21.10.2013 08:26

@catchdave: Вы забыли «вложение»; часть.

Christoffer Hammarström 04.12.2014 12:47

Например, в Node.js с hapi вы можете reply(something).header('Content-Disposition', 'attachment; filename = "' + encodeURI(fileName) + '"')

gdibble 17.05.2016 21:11

В общем, это не что иное, как ответ только по ссылке с 74 голосами "за".

Antti Haapala 12.09.2016 00:33

Следующий документ, связанный с проект RFC, упомянутый Джим в его ответе, дополнительно рассматривает вопрос и определенно заслуживает прямого упоминания здесь:

Тестовые примеры для заголовка HTTP Content-Disposition и кодирования RFC 2231/2047

Обратите внимание, что можно указать оба способа кодирования параметра имени файла, и что они, похоже, правильно работают со старыми браузерами и новыми браузерами (в данном случае старыми являются MSIE8 и Safari). Отметьте attfnboth в отчете, упомянутом @AtifAziz.

Pablo Montilla 11.07.2012 00:43

Есть простая и очень надежная альтернатива: используйте URL-адрес, содержащий желаемое имя файла.

Когда имя после последней косой черты - то, что вам нужно, вам не нужны дополнительные заголовки!

Этот трюк работает:

/real_script.php/fake_filename.doc

И если ваш сервер поддерживает перезапись URL (например, mod_rewrite в Apache), вы можете полностью скрыть часть скрипта.

Символы в URL-адресах должны быть в кодировке UTF-8 побайтно:

/mot%C3%B6rhead   # motörhead

Кто-нибудь знает, как это сделать в ASP.NET? Можно ли без особых проблем сделать что-то вроде GetAttachment.aspx? Id = 34 / fake_filename.doc?

Sean Hanley 31.12.2009 20:24

Попробуйте GetAttachment.aspx / fake_filename.doc? Id = 34 (хотя это может быть причуда только для Apache)

Kornel 01.01.2010 00:24

Вы можете обработать такой путь в IIS, используя либо настраиваемый модуль .Net HttpModule, либо, возможно, параметр UrlRewrite в IIS7.

David 15.07.2010 19:13

это фантастическое решение; действительно мне очень помог. Благодарю.

kristopolous 15.09.2011 00:24

@SeanHanley - также проверьте перезапись URL-адресов для IIS и MVC framework

BerggreenDK 11.08.2012 00:21

Я пошел по кроличьей тропе и попробовал другие решения; попытка найти правильный браузер и версию для правильной установки заголовков - это слишком большой кошмар. Chrome неправильно идентифицировал себя как Safari, который ведет себя совсем не так (разрывы на запятые, если кодируется неправильно). Избавьте себя от проблем, используйте это решение и задайте псевдоним URL-адреса по мере необходимости.

mpen 19.09.2013 21:24

Я сделал это в веб-формах ASP.NET 4.0, используя Маршрутизация ASP.NET. Я зарегистрировал маршрут: routes.MapPageRoute("Download", "download/{id}/{filename}", "~/download.aspx"); В download.aspx я использую только id: Page.RouteData.Values ​​["id"] и не пишу дополнительный заголовок Content-Disposition. Полагаю, работает хорошо и проще, чем HttpModule.

Piper 03.04.2014 17:00

пожалуйста, не кодируйте. на% 2e, ie7 в winxp не сможет отображать правильное имя файла.

bronze man 04.12.2014 08:20

Метод /:id/:filename действительно прост и работает, спасибо!

Luca Steeb 15.11.2015 04:32

как мне реализовать подход url с laravel Response::download()? если я не определяю имя файла в этом методе, он сам выберет имя файла и не учитывает url

alex 18.03.2016 23:52

Тысячу раз «Да». С этим вы серьезно выиграете время. Более того, некоторые браузеры Android заменяют игнорироватьContent-Disposition и вместо этого создают очень интересные имена файлов (они будут сгенерированы из вашего пути). Таким образом, единственное решение для сохранения здравомыслия - это просто установить Content-Disposition: attachment и передать желаемое имя файла в качестве последнего компонента пути:

Julik 29.05.2016 23:09

это отличное решение (и заставило меня почувствовать себя немного глупо) в соответствующей заметке, помните, если имя файла исходит из пользовательской переменной, вам все равно нужно убедиться, что оно готово для файловой системы. Если вы этого не сделаете, и в файле есть что-то вроде /, вы получите странные ошибки браузера В самом деле. С этот ответ в качестве ссылки я использовал s.replace(/[\000-\031\\/:*?"<>\|]/g, '_')

Caleb Hearon 02.08.2016 22:10

Но в этом случае нам нужно заранее знать имя файла, не так ли? Это делает два запроса, один для имени файла, один для самого файла.

Guney Ozsan 22.02.2019 12:27

@GuneyOzsan На уровне HTTP нет абсолютно никакой разницы, и он никогда не вызывает никаких дополнительных запросов. Вам не нужно знать имя файла, вам нужно включить имя файла в URL-адрес, который вы все равно должны знать.

Kornel 24.02.2019 19:30

@Kornel в моем текущем проекте я не знаю заранее имена файлов и запрашиваю файлы по идентификатору и пытаюсь получить файл (как поток) и имя (желательно в заголовке) в одном запросе. С другой стороны, версия C#, которую использует Unity, не поддерживает этот странный синтаксис в Content-Disposition. В конце концов я решил это, закодировав его на php с помощью filename = "' . rawurlencode($file_name_with_extension) . '" и декодировав на C# с помощью headerValue = ContentDispositionHeaderValue.Parse(contentDisposition) и fileName = Uri.UnescapeDataString(headerValue.FileName.Replace("\"", "")).

Guney Ozsan 26.02.2019 15:45

@Kornel Мне было интересно, как и почему fake_filename.doc интерпретируется, как если бы это имя файла в заголовке.

Guney Ozsan 26.02.2019 15:46

@GuneyOzsan имя файла для сохранения определяется веб-браузером, и браузеры не понимают, что происходит на стороне сервера, поэтому они не понимают и не заботятся о том, как сервер интерпретирует URL-адрес. Браузеры просто принимают все, что стоит после последней косой черты в пути URL, иногда дополнительно пытаясь исправить расширения имени файла на основе Content-Type.

Kornel 26.02.2019 21:19

@Kornel Ops, извините, я устал часами работать над исправлением какой-то ошибки и путал «косую черту» с «подчеркиванием», пытаясь понять черную магию, стоящую за тем, почему браузер удаляет часть fake_. Спасибо за уделенное время.

Guney Ozsan 27.02.2019 00:51

в asp.net mvc2 я использую что-то вроде этого:

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

Я думаю, если вы не используете mvc (2), вы можете просто закодировать имя файла, используя

HttpUtility.UrlPathEncode(fileName)

Кодировка URL-адреса для кодировки имени файла недействительна, браузеры не должны декодировать URL-адрес.

SerialSeb 28.04.2011 20:14

IE 11 определенно не декодирует кодировку url в этом поле.

pseudocoder 16.06.2015 18:50

Но он должен быть UrlEncoded, когда браузер Chrome или IE, другие, такие как FF, Safari и Opera, отлично работают без кодирования.

Reza 09.03.2016 21:43

Я знаю, что это старый пост, но он все еще очень актуален. Я обнаружил, что современные браузеры поддерживают rfc5987, который позволяет кодировать utf-8 с процентной кодировкой (с кодировкой url). Тогда Naïve file.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

В 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.

@ Алексей Жуковский: Я не знаю почему, но, как обсуждалось на Соединять, похоже, что это работает не очень хорошо.

Я протестировал приведенный выше код с помощью FF 8.0.1 в Windows 7. Выбран RFC5987, и имя файла (Naïve file.txt) отображается правильно.

Martin Ørding-Thomsen 23.11.2011 12:48

Он работает для Mobile Safari (необработанный utf-8, как предложено выше), но не работает для GoodReader с того же устройства. Есть идеи?

Thilo 08.03.2012 12:15

IE7 и 8 также нуждаются в экранировании апострофов: .Replace ("'", Uri.HexEscape (' \ ''))

TomZ 19.06.2012 22:55

Прямая запись символов UTF-8, похоже, работает для текущих версий Firefox, Chrome и Opera. Не тестировал Safari и IE.

Martin Tournoij 21.01.2013 18:40

Мой Chrome '26 .0,1410,64 м 'не распознает формат rfc5987. Ест старую т.е. процентную кодировку.

ASBai 26.04.2013 00:24

Почему бы не объединить их, как Content-Disposition: attachment; filename*=UTF-8''Na%C3%AFve%20file.txt; filename=Na%C3%AFve%20file.txt, и пропустить поиск в браузере? Это сработает?

Dave Van den Eynde 08.11.2013 18:02

Еще одно дополнение: в IE9 %20 в аргументе filename* приводит не к пробелу, а к буквальному %20 в имени файла.

robinst 11.07.2014 13:23

@Rutix В моем случае полное удаление части имени файла сработало, потому что путь URL-адреса уже включал имя файла. Затем все протестированные браузеры использовали это имя в качестве имени файла (которое работало с пробелами).

robinst 03.03.2015 06:13

Проверено на IE11 Mobile на "Windows Phone 8.1 Update" не работает :-(

oakman 25.04.2015 10:46

@DaveVandenEynde Это не работает в современных браузерах на основе Chromium. Вместо этого браузер показывает предупреждение о проблемах с безопасностью (хотя я не уверен, какие проблемы с безопасностью могут быть вызваны указанием нескольких имен файлов).

Georg 03.06.2015 10:56

@Georg С тех пор я научился полагаться на класс ContentDispositionHeaderValue Web.Api, чтобы справиться с этим за меня.

Dave Van den Eynde 04.06.2015 15:33

Некоторое повторное тестирование этого (с использованием inline;filename=) предполагает, что использование имен файлов, содержащих пробелы, должно иметь символы двойных кавычек ("), чтобы Firefox 42 использовал что-либо, кроме имени файла перед первым пробелом. Использование имен файлов в кодировке URL не работает; имя файла в диалоговом окне «Сохранить как» становится my%20file.txt. То же самое с Safari 9: необходимо использовать кавычки, а% -кодирование - беспорядок. Google Chrome 46, похоже, полностью игнорирует заголовок или, возможно, ему не нравится что-то конкретное в форматировании.

Christopher Schultz 03.12.2015 20:44

Я должен был упомянуть, что кодирование URL не работает специально с filename. Другое дело - использование filename*. Также обратите внимание, что вы не можете использовать + для пробела в имени файла при использовании filename*: вы должны использовать %20.

Christopher Schultz 03.12.2015 21:05

Например, в Node.js с hapi вы можете reply(something).header('Content-Disposition', 'attachment; filename = "' + encodeURI(fileName) + '"')

gdibble 17.05.2016 21:10

Добрые люди из fastmail нашли другой обходной путь: blog.fastmail.com/2011/06/24/download-non-english-filenames Content-Disposition: attachment; filename = "foo-% c3% a4.html"; filename * = UTF-8''foo-% c3% a4.html Если указать имя файла дважды (один раз без префикса UTF-8 и один раз с), он будет работать в IE8-11, Edge, Chrome, Firefox и Safari ( похоже на исправленное сафари Apple, так что теперь оно работает и там)

wullinkm 26.08.2016 12:47

@ MartinØrding-Thomsen Знаете ли вы, почему стандарт System.Net.Mime.ContentDisposition генерирует недопустимое имя, которое не может быть интерпретировано никаким браузером (даже Chrome не может)?

Alex Zhukovskiy 12.09.2016 15:48

@DaveVandenEynde Необходимо отличать Content-Disposition в заголовках запроса от заголовков в теле multipart/form-data. В последнем случае использование filename* явно запрещено для Content-Disposition, см. tools.ietf.org/html/rfc7578#section-4.2.

Brice 12.03.2018 17:46

Я протестировал следующий код во всех основных браузерах, включая старые проводники (в режиме совместимости), и он везде хорошо работает:

$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.'"');

Я использую следующие фрагменты кода для кодирования (предполагая, что имя файла содержит имя файла и расширение файла, например: 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 ) );
}

Ява:

fileName = request.getHeader ( "user-agent" ).contains ( "MSIE" ) ? URLEncoder.encode ( fileName, "utf-8") : MimeUtility.encodeWord ( fileName );
response.setHeader ( "Content-disposition", "attachment; filename=\"" + fileName + "\"");

Правильно, это должно быть rawurlencode в PHP, по крайней мере, для заголовка расположения filename*=, поскольку value-chars, используемый в ext-value RFC 6266-> RFC 5987 (см. tools.ietf.org/html/rfc6266#section-4.1 и tools.ietf.org/html/rfc5987#section-3.2.1), не допускает пространства без процентного экранирования (filename=, с другой стороны, кажется что он может позволить пробел вообще без экранирования, хотя здесь должен присутствовать только ASCII). Нет необходимости кодировать с полной строгостью rawurlencode, поэтому некоторые символы могут быть неэкранированными: gist.github.com/brettz9/8752120

Brett Zamir 01.02.2014 17:06

RFC 6266 описывает «Использование поля заголовка Content-Disposition в протоколе передачи гипертекста (HTTP)». Цитата из этого:

6. Internationalization Considerations

The “filename*” parameter (Section 4.3), using the encoding defined in [RFC5987], allows the server to transmit characters outside the ISO-8859-1 character set, and also to optionally specify the language in use.

И в их раздел примеров:

This example is the same as the one above, but adding the "filename" parameter for compatibility with user agents not implementing RFC 5987:

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

Note: Those user agents that do not support the RFC 5987 encoding ignore “filename*” when it occurs after “filename”.

В Приложение D. также есть длинный список предложений по увеличению интероперабельности. Он также указывает на сайт, который сравнивает реализации. Текущие универсальные тесты, подходящие для общих имен файлов, включают:

  • 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 черновик.

У меня были проблемы с Safari. При загрузке файлов с русскими именами получаются ошибочные и нечитаемые символы. Решение помогло. Но нам нужно отправить заголовок в одну строку (!!!).

evtuhovdo 15.07.2016 13:14

У нас была аналогичная проблема в веб-приложении, и в итоге мы прочитали имя файла из HTML <input type = "file"> и установили его в форме с кодировкой URL в новом HTML <input type = "hidden">. Конечно, нам пришлось удалить путь типа «C: \ fakepath \», который возвращают некоторые браузеры.

Конечно, это не дает прямого ответа на вопрос ОП, но может быть решением для других.

Совершенно другой вопрос. Речь идет о скачивание, ваш ответ - о загрузка.

Oskar Berggren 25.02.2016 19:38

Я получил следующий код в моем скрипте "download.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 и "безопасные" символы; в противном случае он добавляет имя файла * = UTF-8 '' в URL-кодировке. Согласно этот конкретный тестовый пример, он должен работать с MSIE9 и выше, а также с последними версиями FF, Chrome, Safari; в более ранней версии MSIE он должен предлагать имя файла, содержащее версию имени файла ISO8859-1, с подчеркиванием на символах не в этой кодировке.

Заключительное примечание: макс. размер каждого поля заголовка составляет 8190 байт на apache. UTF-8 может содержать до четырех байтов на символ; после rawurlencode это x3 = 12 байт на один символ. Довольно неэффективно, но теоретически возможно иметь более 600 «улыбок»% F0% 9F% 98% 81 в имени файла.

... но максимальная длина передаваемого имени файла также зависит от клиента. Только что выяснил, что самое большее [89 смайлов?] .pdf имя файла проходит через MSIE11. В Firefox37 это не более [111x ?] .pdf. Chrome41 обрезает имя файла на 110-м смайле. Что интересно, суффикс передается нормально.

apurkrt 05.04.2015 19:13

В веб-API ASP.NET я кодирую 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;
    }
}

IE 9 Not fixed
IE 9 Fixed

Поместите имя файла в двойные кавычки. Решил проблему за меня. Так:

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

http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download

Я проверил несколько вариантов. Браузеры не поддерживают спецификации и действуют иначе, я считаю, что двойные кавычки - лучший вариант.

К сожалению, это не решает всех проблем, описанных в ответах выше.

Luca Steeb 06.03.2016 21:24

Это позволит вам вернуть имя файла с пробелами, &, %, # и т. д. Так что это решает.

Don Cheadle 26.08.2016 20:17

Что, если имя файла содержит двойные кавычки (да, это может случиться). Как указано в RFC 6266, имя файла является «строкой в ​​кавычках», и, как указано в RFC 2616, двойные кавычки внутри строки в кавычках должны быть экранированы обратной косой чертой.

Christophe Roussy 19.09.2018 16:55

Если вы используете серверную часть 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);
}

Лучше использовать encodeURI(str). Например, с датами в имени файла: encodeURIComponent('"Kornél Kovács 1/1/2016') => "Kornél Kovács 1% 2F1% 2F2016" vs. encodeURI('"Kornél Kovács 1/1/2016') => "Kornél Kovács 1/1/2016"

gdibble 17.05.2016 21:13

В PHP это сделало это за меня (при условии, что имя файла закодировано в UTF8):

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

Протестировано против IE8-11, Firefox и Chrome.
Если браузер может интерпретировать имя файла * = utf-8, он будет использовать версию имени файла UTF8, иначе он будет использовать декодированное имя файла. Если ваше имя файла содержит символы, которые не могут быть представлены в ISO-8859-1, вы можете вместо этого рассмотреть возможность использования iconv.

Хотя этот код может ответить на вопрос, предоставляя дополнительный контекст относительно Почему и / или как, он отвечает, что вопрос значительно улучшит его долгосрочное значение. Пожалуйста, редактировать свой ответ, чтобы добавить некоторые пояснения.

Toby Speight 20.05.2016 17:32

Ого, ни один из приведенных выше ответов, содержащих только код, не получил голосов или критики. Также я обнаружил, что Почему уже получил достаточно хороший ответ: IE не интерпретирует имя файла * = utf-8, но ему нужна версия имени файла ISO8859-1, которую предлагает этот сценарий. Только хотел дать ленивым работающий простой код для PHP.

Gustav 22.05.2016 18:20

Я думаю, что это было отклонено, потому что вопрос не зависит от языка, а о том, какие RFC следует придерживаться при реализации кодировки заголовка. Однако спасибо за этот ответ, для PHP, этот код избавил меня от проблем.

j4k3 28.06.2016 09:49

Спасибо. Возможно, этот ответ не отвечал строго на вопрос, но это было именно то, что я искал, и помог мне решить проблему в Python.

Lyndsy Simon 07.07.2016 17:43

Я почти уверен, что этот код можно использовать в качестве вектора атаки, если пользователь может контролировать имя файла.

Antti Haapala 12.09.2016 00:35

Классическое решение 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

Благодарим Загрузка файла в чистом формате ASP, реализовав функцию BytesToString() из include_aspuploader.asp в моем собственном коде, и мне удалось заставить работать имена файлов UTF-8.


Полезные ссылки

Просто обновление, так как я пробовал все это сегодня в ответ на проблему клиента

  • За исключением Safari, настроенного для японского языка, все браузеры, протестированные нашим клиентом, лучше всего работали с filename = text.pdf, где текст - это значение клиента, сериализованное ASP.Net/IIS в utf-8 без кодировки URL. По какой-то причине Safari, настроенный для английского языка, примет и правильно сохранит файл с японским именем utf-8, но тот же браузер, настроенный для японского языка, сохранит файл с неинтерпретированными символами utf-8. Все остальные протестированные браузеры работали лучше всего / нормально (независимо от языковой конфигурации) с именем файла utf-8, закодированным без кодировки URL.
  • Я не смог найти ни одного браузера, реализующего Rfc5987 / 8187 вообще. Я тестировал последние версии Chrome, Firefox, а также IE 11 и Edge. Я попытался установить заголовок только с именем filename * = utf-8''texturlencoded.pdf, задав его как filename = text.pdf; имя файла * = utf-8''texturlencoded.pdf. Ни одна функция Rfc5987 / 8187 не обрабатывалась правильно ни в одном из вышеперечисленных.

Это хорошее обновление. Не могли бы вы подробнее рассказать о конкретных тестах, которые вы пробовали?

Brad 16.10.2019 00:27

PHP-фреймворк Symfony 4 имеет $filenameFallback в HeaderUtils::makeDisposition. Вы можете изучить эту функцию для получения подробной информации - она ​​похожа на ответы выше.

Пример использования:

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

Другие вопросы по теме