【问题标题】:Create and parse multipart HTTP requests in Python在 Python 中创建和解析多部分 HTTP 请求
【发布时间】:2011-05-24 23:11:15
【问题描述】:

我正在尝试编写一些 python 代码,它可以在客户端创建多部分 mime http 请求,然后在服务器上进行适当的解释。我认为,我已经在客户端部分成功了:

from email.mime.multipart import MIMEMultipart, MIMEBase
import httplib
h1 = httplib.HTTPConnection('localhost:8080')
msg = MIMEMultipart()
fp = open('myfile.zip', 'rb')
base = MIMEBase("application", "octet-stream")
base.set_payload(fp.read())
msg.attach(base)
h1.request("POST", "http://localhost:8080/server", msg.as_string())

唯一的问题是电子邮件库还包含 Content-Type 和 MIME-Version 标头,我不确定它们将如何与 httplib 包含的 HTTP 标头相关:

Content-Type: multipart/mixed; boundary="===============2050792481=="
MIME-Version: 1.0

--===============2050792481==
Content-Type: application/octet-stream
MIME-Version: 1.0

这可能是当我的 web.py 应用程序收到此请求时,我只收到一条错误消息的原因。 web.py POST 处理程序:

class MultipartServer:
    def POST(self, collection):
        print web.input()

抛出此错误:

Traceback (most recent call last):
  File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/application.py", line 242, in process
    return self.handle()
  File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/application.py", line 233, in handle
    return self._delegate(fn, self.fvars, args)
  File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/application.py", line 415, in _delegate
    return handle_class(cls)
  File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/application.py", line 390, in handle_class
    return tocall(*args)
  File "/home/richard/Development/server/webservice.py", line 31, in POST
    print web.input()
  File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/webapi.py", line 279, in input
    return storify(out, *requireds, **defaults)
  File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/utils.py", line 150, in storify
    value = getvalue(value)
  File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/utils.py", line 139, in getvalue
    return unicodify(x)
  File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/utils.py", line 130, in unicodify
    if _unicode and isinstance(s, str): return safeunicode(s)
  File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/utils.py", line 326, in safeunicode
    return obj.decode(encoding)
  File "/usr/lib/python2.6/encodings/utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode bytes in position 137-138: invalid data

我的代码行由大约一半的错误行表示:

  File "/home/richard/Development/server/webservice.py", line 31, in POST
    print web.input()

它即将到来,但我不知道从这里去哪里。这是我的客户端代码的问题,还是 web.py 的限制(也许它不能支持多部分请求)?任何关于替代代码库的提示或建议都将不胜感激。

编辑

上面的错误是由于数据没有被自动base64编码造成的。添加

encoders.encode_base64(base)

摆脱了这个错误,现在问题很清楚了。 HTTP 请求在服务器中没有被正确解释,可能是因为电子邮件库在正文中包含了应该是 HTTP 标头的内容:

<Storage {'Content-Type: multipart/mixed': u'', 
          ' boundary': u'"===============1342637378=="\n'
          'MIME-Version: 1.0\n\n--===============1342637378==\n'
          'Content-Type: application/octet-stream\n'
          'MIME-Version: 1.0\n' 
          'Content-Transfer-Encoding: base64\n'
          '\n0fINCs PBk1jAAAAAAAAA.... etc

所以有些地方不对劲。

谢谢

理查德

【问题讨论】:

  • @TokenMacGuy - 是的。是的。

标签: python http mime multipart


【解决方案1】:

我使用 Will Holcomb http://pypi.python.org/pypi/MultipartPostHandler/0.1.0 的这个包通过 urllib2 发出多部分请求,它可能会帮助你。

【讨论】:

  • 太好了,谢谢,我去看看。当然有正确的名字:)干杯,R
【解决方案2】:

经过一番探索,这个问题的答案已经很清楚了。简短的回答是,虽然 Content-Disposition is optional 在 Mime 编码的消息中,但 web.py 要求每个 mime 部分都使用它,以便正确解析 HTTP 请求。

与此问题上的其他 cmets 不同,HTTP 和电子邮件之间的区别无关紧要,因为它们只是 Mime 消息的传输机制而已。多部分/相关(不是多部分/表单数据)消息在内容交换 Web 服务中很常见,这就是这里的用例。不过,sn-ps 提供的代码是准确的,这让我找到了一个稍微简短的问题解决方案。

# open an HTTP connection
h1 = httplib.HTTPConnection('localhost:8080')

# create a mime multipart message of type multipart/related
msg = MIMEMultipart("related")

# create a mime-part containing a zip file, with a Content-Disposition header
# on the section
fp = open('file.zip', 'rb')
base = MIMEBase("application", "zip")
base['Content-Disposition'] = 'file; name="package"; filename="file.zip"'
base.set_payload(fp.read())
encoders.encode_base64(base)
msg.attach(base)

# Here's a rubbish bit: chomp through the header rows, until hitting a newline on
# its own, and read each string on the way as an HTTP header, and reading the rest
# of the message into a new variable
header_mode = True
headers = {}
body = []
for line in msg.as_string().splitlines(True):
    if line == "\n" and header_mode == True:
        header_mode = False
    if header_mode:
        (key, value) = line.split(":", 1)
        headers[key.strip()] = value.strip()
    else:
        body.append(line)
body = "".join(body)

# do the request, with the separated headers and body
h1.request("POST", "http://localhost:8080/server", body, headers)

web.py 很好地吸收了这一点,因此很明显 email.mime.multipart 适合创建要通过 HTTP 传输的 Mime 消息,但其标头处理除外。

我的另一个整体问题是可扩展性。这个解决方案和这里提出的其他解决方案都不能很好地扩展,因为它们在捆绑到 mime 消息之前将文件的内容读入变量。更好的解决方案是在内容通过 HTTP 连接通过管道输出时可以按需序列化。解决这个问题对我来说并不紧迫,但如果我能找到解决方案,我会回到这里。

【讨论】:

  • 1.我认为设置标题的首选方法是base.add_header('Content-Disposition','file',name='package',...)。 2. 最好搜索\n\n(也可以搜索\r\n\r\n,例如re.search('\r?\n\r?\n',...)),这样你就不必拆分和加入body。 3. 标题行可以折叠。 4. 从技术上讲,终止标头的\n 不是正文的一部分,尽管这无害。 5. 我不完全确定 RFC 5322 和 RFC 2316 语法是否 100% 兼容(特别是 WRT“字符”与八位字节)。
【解决方案3】:

您的请求存在许多问题。正如 TokenMacGuy 所建议的,multipart/mixed 在 HTTP 中是未使用的;改用 multipart/form-data 。此外,部件应该有一个 Content-disposition 标头。可以在Code Recipes 中找到执行此操作的 python 片段。

【讨论】:

  • 谢谢 - 如您所见,我仍在努力;我还没有弄清楚 multipart/mixed 的设置位置。同样,我还没有使用 Content-Disposition 标头,因为我仍在努力将其放入 HTTP 请求中。我的问题是首先如何构建这样的请求。干杯,R.
  • 见食谱。忘记 email.mime - HTTP 不是电子邮件。
  • 嗨,马丁;仅供参考,我已经证明 HTTP 和电子邮件之间的区别在这里无关紧要 - 它们只是传输,并且在任何一种情况下 mime 都是相同的。请参阅我的替代答案。感谢您的指点。 R
  • 错了,multipart/mixed用在HTTP中:docs.couchdb.org/en/latest/replication/…
  • 即使很少见,python 中也会使用 multipart/mixed。请参阅 Google API:developers.google.com/drive/api/v3/performance#details
猜你喜欢
  • 2018-11-28
  • 2011-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-29
  • 1970-01-01
  • 1970-01-01
  • 2016-01-12
相关资源
最近更新 更多