Кириллица в параметре Filename заголовка Content-Disposition
Добрый день, уважаемы хабра-юзеры.
Возникла следующая проблема. Имеется веб-приложение (клиент на js, сервер на php) и некое файловое хранилище. Чтобы скачать файл из хранилища, клиентская часть веб-приложения шлет запрос на серверную, там выполняется бизнес логика (проверка прав доступа и т.д.), если все ок, то отдается ссылка на файл. Затем клиентская часть делает редирект на эту ссылку. На сервере файлового хранилища стоит nginx, который и отдает файлы. Имена у файлов представляют собой набор символов без смыслового содержания (просто GUID), что не очень нравилось пользователям. Они хотели бы, чтобы при скачивании у файла было такое же имя, как у соответствующей ему сущности в веб-приложении. Поскольку переименовывать файлы очень не хотелось бы по некоторым причинам, было придумано следующее:
1. При формировании ссылки в серверной части веб-приложения к ней цепляется GET-параметр, в котором содержится приемлемое для пользователя имя файла.
2. В конфиге nginx при отдаче файла этот параметр подставляется в заголовок Content-Disposition.
(add_header Content-Disposition 'attachment;Filename=$args';)
Проблемы начались с подстановкой в Content-Disposition русского текста.
Во-первых, firefox при редиректе на хранилище делает urlencode ссылки. И nginx в Content-Disposition подставляет закодированную строку. Соответственно firefox предлагает сохранить файл также под закодированным именем.
Во-вторых, даже если в Content-Disposition будет незакодированная строка, но в UTF-8 с кириллицей, то IE ничего знать не хочет о том, что это UTF-8. Интерпретирует его как cp1251 и имя файла получается с кракозябрами.
В общем, данная схема нормально работает только в хроме. Если я не ошибаюсь, то с проблемой номер 1 (urlencode) можно справиться, если пересобрать nginx из исходников, включив в него модуль ngx_set_misc. Тогда в конфиге nginx можно будет с помощью set_unescape_uri сделать urldecode для имени файла перед тем как вставлять его в заголовок Content-Disposition. Но к такому варианту хотелось бы прибегать в последнюю очередь.
А как решить проблему с IE — вообще не знаю.
В общем, я в тупике. Был бы очень благодарен за совет, может быть есть гораздо более простой способ решения моей задачи, а я его в упор не вижу.
А если отдавать файл при помощи CGI?
Т.е. пользователь обращается: addr/cginame/filename, а у вас идет подмена → addr/cginame?filename, вытаскиваете нужный файл и отдаете…
Нельзя использовать Content-Disposition для задания имени файла, так как он поддерживает только Ascii. Решение — используйте ссылки для скачивания вида:
Выше пишут, что не везде работает. Плюс, само решение в RFC выглядит как какой-то страшный костыль, вроде уродливого quoted printable, который используется в письмах. Плюс, ссылка, которая заканчивается на имя файла, выглядит красивее и читабельнее, чем какое-нибудь download.php?id=XXX
Я привет ссылку, т. к. Вы написали, что C-D поддерживает только ASCII, что неверно.
Недостатки же этого метода вполне понятны. Кстати, в MIME используется другой вариант кодирования non-ASCII строк в заголовках (допускает разные варианты представления: qp, b64, как минимум).
Еще раз пересмотрел RFC, какое же это уродливое решение умудрились протолкнуть в стандарт. Вместо того, чтобы в стандарте HTTP задать единый способ кодирования символов в заголовках, они прикрутили сбоку отдельный RFC для кодирования «Header Field Parameters», причем каждое поле теперь может быть закодировано своей кодировкой, причем еще и с кучей способов расстановки кавычек. Этот RFC производит впечатление, будто его в майкрософт писали.
> Кстати, в MIME используется другой вариант кодирования non-ASCII строк
Это все кривые костыли из эпохи древных 7-битных каналов связи, которые тянут лет 20 и никто не решается выбросить и которые все должны теперь поддерживать. Это печально.
Довольно неприятно, что в разных протоколах используются совершенно разные способы кодирования (в MIME, HTTP, HTTP headers, LDAP).
Сам по себе MIME не так страшен. Куда страшнее, что некоторые товарищи (IBM и MS) преспокойно кладут на этот самый MIME. Вот тогда разбор сообщений становится адом.
Примеры:
1. некодированные данные в заголовках,
2. дублирование заголовков (например, 2 заголовка Subject, в одном данные в cp1251, во втором — в utf8, ичсх оба незакодированы),
3. разрезание адреса на части и кодирование в формате =?UTF-8?B?.....?= кусков email'а с угловыми скобками, а не имени отправителя/получателя,
4. несоответствие Content-Transfer-Encoding и реального способа кодирования.
Это из того, что вспомнил сходу. Но там ещё много разных «приятных» мелочей.
Спасибо так и сделал (в урле путь к файлу, слеш + понятное имя, реврайтом убираю последний сегмент).
Вариант с RFC5987 рабочий, но в IE только начиная с 9, а нужен 8й.