Python:如何通过poster在StringIO中发布数据?

4
params = {'file': open("test.txt", "rb"), 'name': 'upload test'}
datagen, headers = poster.encode.multipart_encode(params)
request = urllib2.Request(upload_url, datagen, headers)
result = urllib2.urlopen(request)

我使用poster库进行HTTP POST请求,它的效果很好,我很满意。

但是我想尝试一些新的东西。就像你在上面看到的那样,如果要发送文件数据,我必须打开一个文件。但是有没有办法不用真正的文件来完成这个操作呢?我们可以使用流(如StringIO)来处理数据,就像处理文件一样,对吧?但是我不太了解poster库。所以,我想知道如何使用流来实现poster库的操作。

补充

实际上,我尝试POST图像数据。我写了以下代码:

from PyQt4 import QtCore, QtGui
from poster.encode import multipart_encode
from poster.streaminghttp import register_openers
import urllib2, os

register_openers()
app = QtGui.QApplication(sys.argv)
pixmap = QtGui.QPixmap("c:/test_img.png")
byte_array = QtCore.QByteArray()
buffer = QtCore.QBuffer(byte_array)
buffer.open(QtCore.QIODevice.WriteOnly)
pixmap.save(buffer, "PNG")
from cStringIO import StringIO
datagen, headers = multipart_encode({"image": StringIO(str(byte_array.toBase64()))})
request = urllib2.Request(upload_url, datagen, headers)
_rnt = urllib2.urlopen(request)

但是,我收到了这个错误:
Traceback (most recent call last):
  File "<pyshell#25>", line 1, in <module>
    _rnt = urllib2.urlopen(request)
  File "C:\Python26\lib\urllib2.py", line 126, in urlopen
    return _opener.open(url, data, timeout)
  File "C:\Python26\lib\urllib2.py", line 397, in open
    response = meth(req, response)
  File "C:\Python26\lib\urllib2.py", line 510, in http_response
    'http', request, response, code, msg, hdrs)
  File "C:\Python26\lib\urllib2.py", line 435, in error
    return self._call_chain(*args)
  File "C:\Python26\lib\urllib2.py", line 369, in _call_chain
    result = func(*args)
  File "C:\Python26\lib\urllib2.py", line 518, in http_error_default
    raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
HTTPError: HTTP Error 500: Internal Server Error

这与你的问题无关,但你意识到你的代码从未关闭text.txt文件,对吗?在CPython中,它可能会在paramsdatagen同时消失时自动关闭,但不能保证;在其他实现中,情况甚至更糟。 - abarnert
可能与您的新问题无关,但是:我认为您不需要对数据进行base64编码,因为它们已经在MIME正文中正确编码 - 您需要在另一侧执行额外的base64解码。此外,为什么要从PNG文件创建一个pixmap,然后将其保存为PNG格式的缓冲区,而不是直接读取PNG文件(或将其传递给“poster”)? - abarnert
你遇到的错误是“HTTP Error 500: Internal Server Error”。这意味着网站服务器或更可能是后端脚本出现了bug。如果该URL通常是可靠的,那么它可能意味着您发送了垃圾数据,超出了任何人预期的范围,从而找到了没有被测试过的bug。尝试保存缓冲区以确保它确实是PNG格式,保存datagenheaders的输出以确保它们是合理的,并进行一个简单的测试,不涉及所有Qt相关内容,只需发送包含所需数据的StringIO即可。 - abarnert
upload_url是一个公开可访问的服务吗?如果不是,您是否拥有源代码并可以将其与设置说明一起发布在某个地方?这将使其他人能够帮助您测试出现问题的原因。 - abarnert
@abarnert 当然,我可以保存为PNG文件并将其发送给海报。但是,我希望减少处理时间,所以尝试了这样做。 :) 我会检查“upload_url”,但这不是我的职责,所以可能需要一些时间。 - Hyun-geun Kim
如果你的其中一位同事负责你正在尝试上传到的网络服务,那么请提交一个错误报告来指出他的工作问题——因为服务仅仅因为输入有误就返回500状态码是不正确的。至于“减少处理时间”——显然,读取PNG文件、将其转换为QPixmap、将该QPixmap渲染到缓冲区中、然后对该缓冲区进行编码和发送所需的时间比直接对PNG文件进行编码和发送要长得多。 - abarnert
1个回答

4
< p > file参数是您传递文件对象的位置。那么,如果您传递类似文件的对象会发生什么呢?

>>> params = {'file': cStringIO.StringIO('upload test data'), 'name': 'upload test'}
>>> datagen, headers = poster.encode.multipart_encode(params)
>>> headers
{'Content-Length': '317', 'Content-Type': 'multipart/form-data; boundary=0c56082b1e134424a918b2b083391467'}

看起来它运行成功了。

文档 是怎么说的?

值可以是作为参数值使用的字符串参数值或文件对象。文件对象必须支持 .read() 并且要么支持 .fileno(),要么同时支持 .seek() 和 .tell()。

所以,你可以使用 StringIO 对象,因为它们支持 seek()tell()

但你也不需要这样做。你应该能够直接使用原始字符串。让我们试一下:

>>> params = {'file': 'upload test data', 'name': 'upload test'}
>>> datagen, headers = poster.encode.multipart_encode(params)
>>> headers
{'Content-Length': '317', 'Content-Type': 'multipart/form-data; boundary=0c56082b1e134424a918b2b083391467'}

看,文档是正确的。


@pydsigner:嗯,这个库被描述为不完整,并且距离稳定API还有一个版本的距离,文档附带警告说它可能与当前版本不匹配... - abarnert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接