使用cgi.FieldStorage解析multipart / form-data;无钥匙

时间:2015-10-01 14:08:34

标签: python python-3.x cgi multipartform-data

以下代码应该能够在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 个答案:

答案 0 :(得分:5)

这些更改将使您的脚本在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 source (在 FS34的< / em>定义评论):

  

参数,全部是可选的:

     

fp:文件指针;默认值:sys.stdin.buffer               (当请求方法是GET时不使用)

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

FS27 定义中不存在灰色部分,因此, FS27 FS34 之间的大多数差异在于<处理< em>字符串(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']

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

...
print(parsed)
...

的产率:

FieldStorage(None, None, [FieldStorage('param1', 'blob', 'value1')])
用于 FS27

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

用于 FS34

答案 1 :(得分:1)

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