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

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

Content-Disposition: attachment; filename=FILENAME

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

Текущая грамматика [RFC 2045] ограничивает значения параметров (и, следовательно, имена файлов Content-Disposition) US-ASCII. Мы признаем большую желательность разрешения произвольных наборов символов в именах файлов, но определение необходимых механизмов выходит за рамки этого документа.

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

В рамках этого вопроса популярными браузерами являются:

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

person Atif Aziz    schedule 18.09.2008    source источник
comment
Он работает для Mobile Safari (необработанный utf-8, как было предложено @Martin Ørding-Thomsen), но это не работает для GoodReader с того же устройства. Любые идеи?   -  person Thilo    schedule 08.03.2012
comment
Также см. этот аналогичный вопрос   -  person juergen d    schedule 30.08.2016
comment
Ответ Корнеля оказался путем наименьшего сопротивления, если вы можете установить последний сегмент пути; соедините это с Content-Disposition: attachment.   -  person Antti Haapala    schedule 12.09.2016


Ответы (18)


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

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

person Jim    schedule 18.09.2008
comment
С быстрым тестом, который реализован Firefox и ужасно сломан в IE: он просто не распознает имя файла * как имя файла и пытается удалить имя файла из типа mime и последней части URL-адреса. - person lapo; 16.02.2011
comment
Это было частично исправлено в IE9. - person Julian Reschke; 09.09.2011
comment
Также обратите внимание, что интернет-черновик (не черновик RFC) был завершен, а окончательный документ - RFC 5987 (greenbytes.de/tech/webdav/rfc5987.html) - person Julian Reschke; 29.09.2011
comment
В связи с этим я обнаружил, что Firefox (версии 4-9 включительно) прерывается, если в имени файла есть запятая (,), например Content-Disposition: filename="foo, bar.pdf". В результате Firefox загружает файл правильно, но сохраняет расширение .part (например, foo,bar.pdf-1.part). Тогда, конечно, файл не откроется правильно, потому что приложение не связано с .part. Другие символы ASCII, похоже, работают нормально. - person catchdave; 11.01.2012
comment
RFC, как известно, неправильно реализован в разных браузерах. IE, Chrome, Fx, особенно стандартный браузер Android, очень сильно отличаются в кодировке Unicode / non-ascii. - person Dennis C; 13.09.2013
comment
@DennisCheung У вас есть ссылка на эти несоответствия в работе браузера? (Я хочу предоставить доказательства проблемы). - person Matthew Schinckel; 08.10.2013
comment
@MatthewSchinckel например kbyanc.blogspot.hk/2010/07/ и digiblog.de/2011/ 04 / android-and-the-download-file-headers - person Dennis C; 08.10.2013
comment
Подробнее о поведении IE см. blogs.msdn.com/b/ieinternals/archive/2010/06/07/ - person EricLaw; 21.10.2013
comment
@catchdave: Вы забыли вложение; часть. - person Christoffer Hammarström; 04.12.2014
comment
Например, в Node.js с hapi вы можете reply(something).header('Content-Disposition', 'attachment; filename="' + encodeURI(fileName) + '"') - person gdibble; 17.05.2016
comment
В общем, это не что иное, как ответ только по ссылке с 74 голосами "за". - person Antti Haapala; 12.09.2016

Я знаю, что это старый пост, но он все еще очень актуален. Я обнаружил, что современные браузеры поддерживают 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.

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

person Martin Ørding-Thomsen    schedule 19.07.2011
comment
Я протестировал приведенный выше код с помощью FF 8.0.1 в Windows 7. Выбран RFC5987 и имя файла (Naïve file.txt) отображается правильно. - person Martin Ørding-Thomsen; 23.11.2011
comment
Он работает для Mobile Safari (необработанный utf-8, как предлагалось выше), но не работает для GoodReader с того же устройства. Любые идеи? - person Thilo; 08.03.2012
comment
IE7 и 8 также нуждаются в экранировании апострофов: .Replace (', Uri.HexEscape (' \ '')) - person TomZ; 19.06.2012
comment
Прямая запись символов UTF-8, похоже, работает для текущих версий Firefox, Chrome и Opera. Не тестировал Safari и IE. - person Martin Tournoij; 21.01.2013
comment
Мой Chrome '26 .0,1410,64 м 'не распознает формат rfc5987. Ест старую т.е. процентную кодировку. - person ASBai; 26.04.2013
comment
Почему бы не объединить их, как Content-Disposition: attachment; filename*=UTF-8''Na%C3%AFve%20file.txt; filename=Na%C3%AFve%20file.txt, и пропустить поиск в браузере? Это сработает? - person Dave Van den Eynde; 08.11.2013
comment
Еще одно дополнение: в IE9 %20 в аргументе filename* приводит не к пробелу, а к литералу %20 в имени файла. - person robinst; 11.07.2014
comment
@Rutix В моем случае полное удаление части имени файла сработало, потому что путь URL-адреса уже включал имя файла. Затем все протестированные браузеры использовали это имя в качестве имени файла (которое работало с пробелами). - person robinst; 03.03.2015
comment
Проверено на IE11 Mobile на Windows Phone 8.1 Обновление не работает :-( - person oakman; 25.04.2015
comment
@DaveVandenEynde Это не работает в современных браузерах на основе Chromium. Браузер вместо этого показывает предупреждение о проблемах безопасности (хотя я не уверен, какие проблемы безопасности могут быть вызваны указанием нескольких имен файлов). - person Georg; 03.06.2015
comment
@Georg С тех пор я научился полагаться на класс ContentDispositionHeaderValue Web.Api, чтобы справиться с этим за меня. - person Dave Van den Eynde; 04.06.2015
comment
Некоторое повторное тестирование этого (с использованием inline;filename=) предполагает, что использование имен файлов, содержащих пробелы, должно иметь символы двойных кавычек ("), чтобы Firefox 42 использовал что-либо, кроме имени файла перед первым пробелом. Использование имен файлов в кодировке URL не работает; имя файла в диалоговом окне «Сохранить как» становится my%20file.txt. То же самое с Safari 9: нужно использовать кавычки, а% -кодирование - беспорядок. Google Chrome 46, похоже, полностью игнорирует заголовок или, возможно, ему не нравится что-то конкретное в форматировании. - person Christopher Schultz; 03.12.2015
comment
Я должен был упомянуть, что кодирование URL не работает с filename специально. Другое дело - использование filename*. Также обратите внимание, что вы не можете использовать + вместо пробела в имени файла при использовании filename*: вы должны использовать %20. - person Christopher Schultz; 03.12.2015
comment
Например, в Node.js с hapi вы можете reply(something).header('Content-Disposition', 'attachment; filename="' + encodeURI(fileName) + '"') - person gdibble; 17.05.2016
comment
Добрые люди из fastmail нашли другой обходной путь: blog.fastmail. ru / 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, так что теперь оно работает и там) - person wullinkm; 26.08.2016
comment
@ MartinØrding-Thomsen Знаете ли вы, почему стандартный System.Net.Mime.ContentDisposition генерирует недопустимое имя, которое не может быть интерпретировано никаким браузером (даже Chrome не может)? - person Alex Zhukovskiy; 12.09.2016
comment
@DaveVandenEynde Необходимо отличать Content-Disposition в заголовках запроса от заголовков в теле a multipart/form-data. В последнем случае использование filename* явно запрещено для Content-Disposition, см. tools.ietf.org/html/rfc7578#section-4.2. - person Brice; 12.03.2018

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

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

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

/real_script.php/fake_filename.doc

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

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

/mot%C3%B6rhead   # motörhead
person Kornel    schedule 19.10.2008
comment
Кто-нибудь знает, как это сделать в ASP.NET? Можно ли без особых проблем сделать что-то вроде GetAttachment.aspx? Id = 34 / fake_filename.doc? - person Sean Hanley; 31.12.2009
comment
Попробуйте GetAttachment.aspx / fake_filename.doc? Id = 34 (хотя это может быть причуда только для Apache) - person Kornel; 01.01.2010
comment
Вы можете обработать такой путь в IIS, используя либо настраиваемый модуль .Net HttpModule, либо, возможно, параметр UrlRewrite в IIS7. - person David; 15.07.2010
comment
это фантастическое решение; действительно мне очень помог. Благодарю. - person kristopolous; 15.09.2011
comment
@SeanHanley - также проверьте перезапись URL-адресов для IIS и MVC framework - person BerggreenDK; 11.08.2012
comment
Я пошел по кроличьей тропе и попробовал другие решения; попытка найти правильный браузер и версию для правильной установки заголовков - это слишком большой кошмар. Chrome неправильно идентифицировал себя как Safari, который ведет себя совсем не так (разрывается на запятые, если кодируется неправильно). Избавьтесь от проблем, используйте это решение и задайте псевдоним URL-адреса по мере необходимости. - person mpen; 19.09.2013
comment
Я сделал это в веб-формах ASP.NET 4.0, используя Маршрутизация ASP.NET. Я зарегистрировал маршрут: routes.MapPageRoute("Download", "download/{id}/{filename}", "~/download.aspx"); В download.aspx я использую только идентификатор: Page.RouteData.Values ​​[id] и не пишу дополнительный заголовок Content-Disposition. Полагаю, работает хорошо и проще, чем HttpModule. - person Piper; 03.04.2014
comment
пожалуйста, не кодируйте. на% 2e, ie7 в winxp не сможет отображать правильное имя файла. - person bronze man; 04.12.2014
comment
Метод /:id/:filename действительно прост и работает, спасибо! - person Luca Steeb; 15.11.2015
comment
как мне реализовать подход url с laravel Response::download()? если я не определяю имя файла в этом методе, он сам выберет имя файла и не учитывает url - person alex; 18.03.2016
comment
Тысячу раз да. С этим вы серьезно выиграете время. Более того, некоторые браузеры Android будут полностью игнорировать Content-Disposition и вместо этого создавать очень интересные имена файлов (они будут сгенерированы из вашего пути). Таким образом, единственное решение для сохранения здравомыслия - это просто установить Content-Disposition: attachment и передать желаемое имя файла в качестве последнего компонента пути: - person Julik; 29.05.2016
comment
это отличное решение (и заставило меня почувствовать себя немного глупо) в соответствующей заметке, помните, что если имя файла исходит из пользовательской переменной, вам все равно нужно убедиться, что оно готово для файловой системы. Если вы этого не сделаете и в файле будет что-то вроде /, вы получите действительно странные ошибки браузера. Используя этот ответ в качестве справки, я использовал s.replace(/[\000-\031\\\/:*?"<>\|]/g, '_') - person Caleb Hearon; 02.08.2016
comment
Но в этом случае нам нужно заранее знать имя файла, не так ли? Это делает два запроса, один для имени файла, один для самого файла. - person Guney Ozsan; 22.02.2019
comment
@GuneyOzsan На уровне HTTP нет абсолютно никакой разницы, и он никогда не вызывает никаких дополнительных запросов. Вам не нужно знать имя файла, вам нужно включить имя файла в URL-адрес, который вы все равно должны знать. - person Kornel; 24.02.2019
comment
@Kornel в моем текущем проекте я не знаю заранее имена файлов и запрашиваю файлы по идентификатору и пытаюсь получить файл (как поток) и имя (желательно в заголовке) в одном запросе. С другой стороны, версия C #, которую использует Unity, не поддерживает этот странный синтаксис в Content-Disposition. В конце концов я решил это, закодировав его в php с помощью filename="' . rawurlencode($file_name_with_extension) . '" и декодировав на C # с помощью headerValue = ContentDispositionHeaderValue.Parse(contentDisposition) и fileName = Uri.UnescapeDataString(headerValue.FileName.Replace("\"", "")). - person Guney Ozsan; 26.02.2019
comment
@Kornel Мне действительно было интересно, как и почему fake_filename.doc интерпретируется, как если бы это имя файла в заголовке. - person Guney Ozsan; 26.02.2019
comment
@GuneyOzsan имя файла для сохранения определяется веб-браузером, и браузеры не понимают, что происходит на стороне сервера, поэтому они не понимают и не заботятся о том, как сервер интерпретирует URL-адрес. Браузеры просто принимают все, что стоит после последней косой черты в пути URL, иногда дополнительно пытаясь исправить расширения имени файла на основе Content-Type. - person Kornel; 26.02.2019
comment
@Kornel Ops, извините, я устал часами работать над исправлением какой-то ошибки и перепутал «косую черту» с «подчеркиванием», пытаясь понять черную магию, стоящую за тем, почему браузер удаляет часть fake_. Спасибо за уделенное время. - person Guney Ozsan; 27.02.2019
comment
Подход с именем файла в пути не работает, если имя файла длинное в мобильном сафари ... - person Pavel; 16.06.2021

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 также есть длинный список предложений по повышению совместимости. Он также указывает на сайт, на котором сравниваются реализации. Текущие универсальные тесты, подходящие для общих имен файлов, включают:

  • attwithisofnplain: простое имя файла ISO-8859-1 с двойными кавычками и без кодировки. Для этого требуется имя файла, которое соответствует стандарту ISO-8859-1 и не содержит знаков процента, по крайней мере, не перед шестнадцатеричными цифрами.
  • attfnboth: два параметра в порядке, описанном выше. Должен работать для большинства имен файлов в большинстве браузеров, хотя IE8 будет использовать параметр «filename».

Этот RFC 5987, в свою очередь, ссылается на RFC 2231, в котором описан фактический формат. 2231 в первую очередь предназначен для почты, а 5987 сообщает нам, какие части также могут использоваться для заголовков HTTP. Не путайте это с заголовками MIME, используемыми внутри multipart/form-data HTTP body, который регулируется RFC 2388 (в частности, раздел 4.4) и черновик HTML 5.

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

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

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

person Atif Aziz    schedule 18.09.2008
comment
Обратите внимание, что можно указать оба способа кодирования параметра имени файла, и что они, похоже, правильно работают со старыми браузерами и новыми браузерами (в данном случае старыми являются MSIE8 и Safari). Проверьте attfnboth в отчете, упомянутом @AtifAziz. - person Pablo Montilla; 11.07.2012

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

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

http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download

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

person Dmitry Kaigorodov    schedule 10.07.2015
comment
К сожалению, это не решает всех проблем, описанных в ответах выше. - person Luca Steeb; 06.03.2016
comment
Это позволит вам вернуть имя файла с пробелами, &, %, # и т. Д. Так что это решает. - person Don Cheadle; 26.08.2016
comment
Что, если имя файла содержит двойные кавычки (да, это может случиться). Как указано в RFC 6266, имя файла представляет собой строку в кавычках, и, как указано в RFC 2616, двойные кавычки внутри строки в кавычках должны быть экранированы обратной косой чертой. - person Christophe Roussy; 19.09.2018

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

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

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

HttpUtility.UrlPathEncode(fileName)
person Elmer    schedule 15.07.2010
comment
Кодировка URL-адреса для кодировки имени файла недействительна, браузеры не должны декодировать URL-адрес. - person SerialSeb; 28.04.2011
comment
IE 11 определенно не декодирует кодировку url в этом поле. - person pseudocoder; 16.06.2015
comment
Но он должен быть UrlEncoded, когда браузер Chrome или IE, другие, такие как FF, Safari и Opera, отлично работают без кодирования. - person Reza; 09.03.2016

Я использую следующие фрагменты кода для кодирования (предполагая, что 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 + "\"");
person Vasilen Donchev    schedule 19.04.2013
comment
Правильно, это должно быть 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 - person Brett Zamir; 01.02.2014

В веб-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 фиксированный

person martinoss    schedule 25.06.2015

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

person Gustav    schedule 20.05.2016
comment
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно почему и / или как он отвечает на вопрос, значительно улучшит его долгосрочную ценность. Пожалуйста, отредактируйте свой ответ, чтобы добавить пояснения. - person Toby Speight; 20.05.2016
comment
Ого, ни один из приведенных выше ответов, содержащих только код, не получил отрицательной оценки или критики. Также я обнаружил, что на почему уже достаточно хорошо ответили: IE не интерпретирует имя файла * = utf-8, но ему нужна версия имени файла ISO8859-1, которую предлагает этот скрипт. Только хотел дать ленивым работающий простой код для PHP. - person Gustav; 22.05.2016
comment
Я думаю, что это было отклонено, потому что вопрос не зависит от языка, а о том, какие RFC следует придерживаться при реализации кодировки заголовка. Однако спасибо за этот ответ, для PHP, этот код избавил меня от моих бед. - person j4k3; 28.06.2016
comment
Спасибо. Этот ответ, возможно, не отвечал строго на вопрос, но это было именно то, что я искал, и помог мне решить проблему в Python. - person Lyndsy Simon; 07.07.2016
comment
Я почти уверен, что этот код можно использовать в качестве вектора атаки, если пользователь может контролировать имя файла. - person Antti Haapala; 12.09.2016

Если вы используете серверную часть 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);
}
person Emanuele Spatola    schedule 25.09.2015
comment
Лучше использовать 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 - person gdibble; 17.05.2016

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

$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.'"');
person Stano    schedule 31.05.2012

Я получил следующий код в моем скрипте "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 в имени файла.

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

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

  • За исключением 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 не обрабатывалась правильно ни в одном из вышеперечисленных.
person user1664043    schedule 13.03.2019
comment
Это хорошее обновление. Не могли бы вы подробнее рассказать о конкретных тестах, которые вы пробовали? - person Brad; 16.10.2019

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);
person luchaninov    schedule 22.07.2019

Классическое решение 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 работать с именами файлов.


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

person user692942    schedule 23.05.2016

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

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

person Andrei I    schedule 27.01.2015
comment
Совершенно другой вопрос. Речь идет о загрузке, ваш ответ - о загрузке. - person Oskar Berggren; 25.02.2016

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

person Dario Solera    schedule 18.09.2008
comment
Я тестировал несколько, и это не работает во всех браузерах, поэтому вопрос. :) - person Atif Aziz; 18.09.2008