使用PyCurl从文件对象上传文件

1 投票
1 回答
7373 浏览
提问于 2025-04-15 22:53

我正在尝试上传一个文件,像这样:

import pycurl

c = pycurl.Curl()

values = [
     ("name", "tom"),
     ("image", (pycurl.FORM_FILE, "tom.png"))
]

c.setopt(c.URL, "http://upload.com/submit")
c.setopt(c.HTTPPOST, values)
c.perform()
c.close()

这样做是没问题的。不过,这种方法只适用于本地文件。如果我想要获取一个图片,像这样:

import urllib2
resp = urllib2.urlopen("http://upload.com/people/tom.png")

那我该如何把 resp.fp 作为一个文件对象传递,而不是先把它写入文件再传文件名呢?这样做可以吗?

1 个回答

4

在理想情况下,可能可以把两个数据流直接连接起来,但这样做并不是一个很稳妥的解决方案。这里面有很多麻烦的边界情况:

  • 响应的连接可能还在接收数据,或者出现了卡顿,这样会导致你无法继续发送数据,进而中断POST请求(因为PycURL并不期待在当前“文件”的末尾之后还要等待数据)。
  • 响应可能会重置,这样你就没有完整的文件了,但你已经发送了一部分数据——这种情况下该怎么办呢?
  • 你用urllib获取的文件可能是分块编码的,所以你需要对MIME头进行一些操作才能重新组装——你不能只是盲目地转发数据。
  • 你可能不知道你获取的文件有多大,因此很难在POST请求中提供正确的内容长度,这样你就得使用分块传输。
  • 还有很多其他问题,我现在想不起来了……

你最好是先把文件暂时写到磁盘上,然后在确认你已经获取了完整文件后再进行POST请求。

如果你真的想这样做,最好的办法可能是实现一个自己的类,像文件一样管理这两个连接之间的桥梁(可以正确地缓冲、处理解码等)。

编辑:

根据你留下的评论——没错,你只需要设置 READFUNCTION。可以查看这个文件上传的示例:

http://pycurl.cvs.sourceforge.net/viewvc/pycurl/pycurl/examples/file_upload.py?revision=1.5&view=markup

它正是通过在文件对象上做一个小的封装,并使用回调来读取数据,或者如果你不需要处理任何东西,可以直接把 READFUNCTION 的回调设置为 fp.read

撰写回答