我想使用 Python Requests从 url 获取文件并将其用作 post 请求中的多部分编码文件的库。问题是该文件可能非常大 (50MB-2GB),我不想将它加载到内存中。 (上下文 here .)
文档中的以下示例(multipart、stream down 和 stream up)我编造了这样的东西:
with requests.get(big_file_url, stream=True) as f:
requests.post(upload_url, files={'file': ('filename', f.content)})
但我不确定我是否做对了。它实际上抛出了这个错误——从回溯中编辑:
with requests.get(big_file_url, stream=True) as f:
AttributeError: __exit__
有什么建议吗?
最佳答案
正如其他答案已经指出的那样:requests
doesn't support POSTing multipart-encoded files without loading them into memory .
要使用 multipart/form-data 上传大文件而不将其加载到内存中,您可以使用 poster
:
#!/usr/bin/env python
import sys
from urllib2 import Request, urlopen
from poster.encode import multipart_encode # $ pip install poster
from poster.streaminghttp import register_openers
register_openers() # install openers globally
def report_progress(param, current, total):
sys.stderr.write("\r%03d%% of %d" % (int(1e2*current/total + .5), total))
url = 'http://example.com/path/'
params = {'file': open(sys.argv[1], "rb"), 'name': 'upload test'}
response = urlopen(Request(url, *multipart_encode(params, cb=report_progress)))
print response.read()
它可以被修改以允许 GET 响应对象而不是本地文件:
import posixpath
import sys
from urllib import unquote
from urllib2 import Request, urlopen
from urlparse import urlsplit
from poster.encode import MultipartParam, multipart_encode # pip install poster
from poster.streaminghttp import register_openers
register_openers() # install openers globally
class MultipartParamNoReset(MultipartParam):
def reset(self):
pass # do nothing (to allow self.fileobj without seek() method)
get_url = 'http://example.com/bigfile'
post_url = 'http://example.com/path/'
get_response = urlopen(get_url)
param = MultipartParamNoReset(
name='file',
filename=posixpath.basename(unquote(urlsplit(get_url).path)), #XXX \ bslash
filetype=get_response.headers['Content-Type'],
filesize=int(get_response.headers['Content-Length']),
fileobj=get_response)
params = [('name', 'upload test'), param]
datagen, headers = multipart_encode(params, cb=report_progress)
post_response = urlopen(Request(post_url, datagen, headers))
print post_response.read()
此解决方案需要 GET 响应中的有效 Content-Length
header (已知文件大小)。如果文件大小未知,则可以使用分 block 传输编码来上传多部分/表单数据内容。使用 requests
库附带的 urllib3.filepost
可以实现类似的解决方案,例如,基于 @AdrienF's answer不使用 poster
。
关于python - 使用 Python 请求 'bridge' 文件而不加载到内存中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15973204/