Когда я использую cgi.FieldStorage
для анализа запроса multipart/form-data
(или любой веб-структуры, такой как Pyramid, которая использует cgi.FieldStorage
) У меня есть проблема с загрузкой файлов с определенных клиентов, которые не содержат filename=file.ext
в заголовке Content-Disposition
.cgi.FieldStorage с multipart/form-data пытается декодировать двоичный файл как UTF-8, если «filename =» не указан
Если параметр отсутствует, FieldStorage()
пытается декодировать содержимое файла как UTF-8 и возвращать строку. И очевидно, что многие файлы являются двоичными, а не UTF-8 и как таковые дают фиктивные результаты.
Например:
>>> import cgi
>>> import io
>>> body = (b'--KQNTvuH-itP09uVKjjZiegh7\r\n' +
... b'Content-Disposition: form-data; name=payload\r\n\r\n' +
... b'\xff\xd8\xff\xe0\x00\x10JFIF')
>>> env = {
... 'REQUEST_METHOD': 'POST',
... 'CONTENT_TYPE': 'multipart/form-data; boundary=KQNTvuH-itP09uVKjjZiegh7',
... 'CONTENT_LENGTH': len(body),
... }
>>> fs = cgi.FieldStorage(fp=io.BytesIO(body), environ=env)
>>> (fs['payload'].filename, fs['payload'].file.read())
(None, '����\x00\x10JFIF')
браузеров и наиболее HTTP библиотеки сделать включает вариант для загрузки файлов, но я в настоящее время имеет дело с клиентом, который не (и опуская filename
делает кажутся действительными в соответствии со спецификацией).
В настоящее время я использую довольно Hacky обходной путь подклассов FieldStorage
и заменив соответствующий Content-Disposition
заголовок с тем, который действительно имеет имя файла:
import cgi
import os
class FileFieldStorage(cgi.FieldStorage):
"""To use, subclass FileFieldStorage and override _file_fields with a tuple
of the names of the file field(s). You can also override _file_name with
the filename to add.
"""
_file_fields =()
_file_name = 'file_name'
def __init__(self, fp=None, headers=None, outerboundary=b'',
environ=os.environ, keep_blank_values=0, strict_parsing=0,
limit=None, encoding='utf-8', errors='replace'):
if self._file_fields and headers and headers.get('content-disposition'):
content_disposition = headers['content-disposition']
key, pdict = cgi.parse_header(content_disposition)
if (key == 'form-data' and pdict.get('name') in self._file_fields and
'filename' not in pdict):
del headers['content-disposition']
quoted_file_name = self._file_name.replace('"', '\\"')
headers['content-disposition'] = '{}; filename="{}"'.format(
content_disposition, quoted_file_name)
super().__init__(fp=fp, headers=headers, outerboundary=outerboundary,
environ=environ, keep_blank_values=keep_blank_values,
strict_parsing=strict_parsing, limit=limit,
encoding=encoding, errors=errors)
Использование body
и env
в моем первом тесте, это теперь работает :
>>> class TestFieldStorage(FileFieldStorage):
... _file_fields = ('payload',)
>>> fs = TestFieldStorage(fp=io.BytesIO(body), environ=env)
>>> (fs['payload'].filename, fs['payload'].file.read())
('file_name', b'\xff\xd8\xff\xe0\x00\x10JFIF')
есть ли способ избежать этого хака и сказать FieldStorage
не декодировать, как UTF-8? Было бы неплохо, если бы вы могли предоставить encoding=None
или что-то в этом роде, но это не похоже на то, что это поддерживает.
Установка ошибок = 'surrogateescape' и затем делать' string.encode ('UTF-8', 'surrogateescape') 'хороший хак, спасибо! –