Подтвердить что ты не робот

Как кодировать имя файла UTF8 для заголовков HTTP? (Python, Django)

У меня проблема с HTTP-заголовками, они закодированы в ASCII, и я хочу предоставить представление для загрузки файлов, имена которых могут быть не ASCII.

response['Content-Disposition'] = 'attachment; filename="%s"' % (vo.filename.encode("ASCII","replace"), )

Я не хочу использовать статические файлы для одной и той же проблемы с именами файлов без ASCII, но в этом случае возникнет проблема с файловой системой и кодировкой имени файла. (Я не знаю цели.)

Я уже пробовал urllib.quote(), но вызывает исключение KeyError.

Возможно, я делаю что-то неправильно, но, возможно, это невозможно.

4b9b3361

Ответ 1

Это FAQ.

Существует не совместимый способ сделать это. Некоторые браузеры реализуют проприетарные расширения (IE, Chrome), другие реализуют RFC 2231 (Firefox, Opera).

См. тестовые примеры http://greenbytes.de/tech/tc2231/.

Обновление: по состоянию на ноябрь 2012 года все текущие настольные браузеры поддерживают кодировку, определенную в RFC 6266 и RFC 5987 (Safari >= 6, IE >= 9, Chrome, Firefox, Opera, Konqueror).

Ответ 2

Не отправляйте имя файла в Content-Disposition. Невозможно настроить кросс-браузер (*) параметры не-ASCII-заголовка.

Вместо этого отправьте только "Content-Disposition: attachment" и оставьте имя файла в виде строки UTF-8, закодированной в URL-адресе в концевой (PATH_INFO) части вашего URL-адреса, чтобы браузер мог выбирать и использовать по умолчанию. URL-адреса UTF-8 обрабатываются гораздо более надежно браузерами, чем что-либо, что связано с Content-Disposition.

(*: на самом деле, нет даже текущего стандарта, который говорит, как это должно быть сделано, поскольку отношения между RFC 2616, 2231 и 2047 довольно дисфункциональны, то, что Джулиан пытается прояснить на уровне спецификации. поддержка браузера в отдаленном будущем.)

Ответ 3

Обратите внимание, что в 2011 году RFC 6266 (особенно Приложение D) в этом вопросе было сказано и содержит конкретные рекомендации.

А именно, вы можете выпустить filename только с символами ASCII, а затем filename* с файловым именем формата RFC 5987 для тех агентов, которые его понимают.

Обычно это будет выглядеть как filename="my-resume.pdf"; filename*=UTF-8''My%20R%C3%A9sum%C3%A9.pdf, где имя файла Unicode ( "My Résumé.pdf" ) закодировано в UTF-8, а затем в процентах (обратите внимание, НЕ используйте + для пробелов).

Пожалуйста, действительно прочитайте RFC 6266 и RFC 5987 (или используйте надежную и протестированную библиотеку, которая абстрагирует это для вас), так как мое резюме здесь не содержит важных деталей.

Ответ 4

Я могу сказать, что у меня был успех с использованием нового (RFC 5987) формата указания заголовка, закодированного с помощью электронной почты form (RFC 2231). Я придумал следующее решение, основанное на коде из проекта django-sendfile.

import unicodedata
from django.utils.http import urlquote

def rfc5987_content_disposition(file_name):
    ascii_name = unicodedata.normalize('NFKD', file_name).encode('ascii','ignore').decode()
    header = 'attachment; filename="{}"'.format(ascii_name)
    if ascii_name != file_name:
        quoted_name = urlquote(file_name)
        header += '; filename*=UTF-8\'\'{}'.format(quoted_name)

    return header

# e.g.
  # request['Content-Disposition'] = rfc5987_content_disposition(file_name)

Я только проверил свой код на Python 3.4 с Django 1.8. Таким образом, аналогичное решение в django-sendfile может вам лучше поменять.

Там есть длинный билет в джэкго-трекере, который подтверждает это, но патчи еще не были предложены afaict. К сожалению, это так близко к использованию надежной проверенной библиотеки, как я мог найти, пожалуйста, дайте мне знать, если есть лучшее решение.

Ответ 5

Начиная с 2018 года, решение теперь доступно в Django 2.1 (после томления в течение семи лет в виде открытого билета). Вы можете использовать параметр as_attachment встроенный в FileResponse. Например, чтобы вернуть файл output_file с типом mime output_mime_type в качестве ответа HTTP:

response = FileResponse(open(output_file, 'rb'), as_attachment=True, content_type=output_mime_type)
return response

Или, если вы не можете использовать FileResponse, вы можете использовать соответствующую часть из ее источника для более непосредственного изменения Content-Disposition. Вот как выглядит этот источник в настоящее время:

from urllib.parse import quote
try:
    document.file_name.encode('ascii')
    file_expr = 'filename="{}"'.format(filename)
except UnicodeEncodeError:
    # Handle a non-ASCII filename
    file_expr = "filename*=utf-8''{}".format(quote(filename))
response['Content-Disposition'] = 'attachment; {}'.format(file_expr)

Ответ 6

Взлом:

if (Request.UserAgent.Contains("IE"))
{
  // IE will accept URL encoding, but spaces don't need to be, and since they're so common..
  filename = filename.Replace("%", "%25").Replace(";", "%3B").Replace("#", "%23").Replace("&", "%26");
}