使用 cgi.FieldStorage 解析 multipart/form-data; None 键

Parse multipart/form-data using cgi.FieldStorage; None keys

下面这段代码应该可以运行 in 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. 我怀疑内部发生了一些变化,我现在使用错误。但是我似乎无法弄清楚是什么。感谢任何帮助。

在 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--
"""

这些更改将使您的脚本在 Python 2.7.x 和 3.4.x:

中相同地工作

(我将这些缩写用于 cgi.FieldStorage():Python 2.7.x:FS27,Python 3.4.x: FS34)

1 - 虽然 FS27 正确处理边界前的换行符,但 FS34[ 并非如此=81=] 所以解决方案是直接从你的边界(spam)开始。

body = """--spam
Content-Disposition: form-data; name="param1"; filename=blob
Content-type: binary/octet-stream

value1
--spam--
"""

2 - 引用自 cgi.py source (在 FS34 的 定义注释中):

Arguments, all optional:

fp : file pointer; default: sys.stdin.buffer (not used when the request method is GET)

        Can be :
        1. a TextIOWrapper object
        2. an object whose read() and readline() methods return bytes

FS27定义中没有灰色部分,因此,FS27FS34之间的大部分差异 在于 strings(FS27)binary streams(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 keyvaluebody 长度(包括 start/end边界)。


这些修改结合起来会产生预期的结果:

$ python script.py
['param1']
$ python3 script.py
['param1']

作为概念验证,这些是 FS27 返回的 parsed 对象]FS34:

...
print(parsed)
...

产量:

FieldStorage(None, None, [FieldStorage('param1', 'blob', 'value1')])

对于 FS27

FieldStorage(None, None, [FieldStorage('param1', 'blob', b'value1')])

对于 FS34