Parse multipart / form-данные с помощью cgi.FieldStorage; нет ключей
следующий фрагмент кода должен иметь возможность работать в Python 2.7 и Python 3.x.
from __future__ import unicode_literals
from __future__ import print_function
import cgi
try:
from StringIO import StringIO as IO
except ImportError:
from io import BytesIO as IO
body = """
--spam
Content-Disposition: form-data; name="param1"; filename=blob
Content-Type: binary/octet-stream
value1
--spam--
"""
parsed = cgi.FieldStorage(
IO(body.encode('utf-8')),
headers={'content-type': 'multipart/form-data; boundary=spam'},
environ={'REQUEST_METHOD': 'POST'})
print([key for key in parsed])
в Python 2.7 работает нормально и выводит ['param1']
. Однако в Python 3.4, он выводит [None]
.
Я не могу сделать FieldStorage
чтобы получить полезный результат в Python 3. Я подозреваю, что что-то внутренне изменилось, и теперь я использую его неправильно. Однако я не могу понять, что именно. Любая помощь приветствуется.
2 ответов
эти изменения сделают ваш скрипт работать одинаково в обоих Python 2.7.x и 3.4.x:
(я буду использовать эти аббревиатуры для cgi.FieldStorage()
: Python 2.7.x:FS27, Python 3.4.x:FS34)
1 - а FS27 обрабатывает новую строку перед границей правильно, это не относится к FS34 поэтому решение начать с границы(spam
) напрямую.
body = """--spam
Content-Disposition: form-data; name="param1"; filename=blob
Content-type: binary/octet-stream
value1
--spam--
"""
2 - цитировать от cgi.py источник (in FS34 это комментарии к определению):
аргументы, все необязательные:
FP: указатель файла; по умолчанию: sys.стандартный ввод.буфер (не используется, когда метод запроса GET)
Can be : 1. a TextIOWrapper object 2. an object whose read() and readline() methods return bytes
серая часть отсутствует в FS27 определение, так, большая часть различия между FS27 и FS34 лежать в обращении строки(FS27) и двоичные потоки(FS34).
в этой связи FS34 может легко испортить семантику анализируемого объекта, если ему не даны правильные указания о том, как правильно обращаться с этим. Видимо,headers
запись в словаре 'content-type': 'multipart/form-data; boundary=spam'
недостаточно; вы должны предоставить информацию о длине сообщения.
вы можете добейся этого,эффективно, добавив вторую запись в headers
:
headers={'content-type': 'multipart/form-data; boundary=spam;',
'content-length': len(body)}
здесь стоимостью на content-length
ключ - это body
длина (включая границы начала/конца).
эти изменения, в сочетании, приводят к желаемому результату:
$ python script.py
['param1']
$ python3 script.py
['param1']
As доказательств -- принципиальной схемы, эти возвращенные parsed
предметов FS27 и FS34:
...
print(parsed)
...
выходы:
FieldStorage(None, None, [FieldStorage('param1', 'blob', 'value1')])
на FS27 и
FieldStorage(None, None, [FieldStorage('param1', 'blob', b'value1')])
на FS34.
в Python 2.7 и Python 3.5 (по какой-то причине не работает в Python 3.4) желаемый результат возвращается путем добавления Content-Length
в тело ответа:
body = """
--spam
Content-Disposition: form-data; name="param1"; filename=blob
Content-Length: 6
Content-Type: binary/octet-stream
value1
--spam--
"""