作者:万万558 | 来源:互联网 | 2023-02-13 10:07
我试图从Web获取一个大文件,并将其直接流式传输到zipfile
模块提供的zipfile编写器,如:
from urllib.request import urlopen
from zipfile import ZipFile
zip_file = ZipFile("/a/certain/local/zip/file.zip","a")
entry = zip_file.open("an.entry","w")
entry.write( urlopen("http://a.certain.file/on?the=web") )
显然,这不起作用,因为.write
接受一个bytes
参数,而不是I/O读者.但是,由于文件相当大,我不想在压缩之前将整个文件加载到RAM中.
简单的解决方案是使用bash(从未真正尝试过,可能是错误的):
curl -s "http://a.certain.file/on?the=web" | zip -q /a/certain/local/zip/file.zip
但是在Python脚本中放置一行bash并不是一件非常优雅,也不方便的事情.
另一个解决方案是使用urllib.request.urlretrieve
下载文件,然后传递路径zipfile.ZipFile.open
,但这样我仍然需要等待下载完成,此外还会消耗更多的磁盘I/O资源.
有没有办法,在Python中,直接将下载流传递给zipfile编写器,如上面的bash管道?
1> Martijn Piet..:
您可以使用shutil.copyfileobj()
在文件对象之间有效地复制数据:
from shutil import copyfileobj
with ZipFile("/a/certain/local/zip/file.zip", "w") as zip_file:
with zip_file.open("an.entry", "w") as entry:
with urlopen("http://a.certain.file/on?the=web") as response:
shutil.copyfileobj(response, entry)
这将.read()
使用给定的chunksize 调用源文件对象,然后将该块传递给.write()
目标文件对象上的方法.
如果您使用的是Python 3.5或更早版本(您无法直接写入ZipFile
成员),您唯一的选择是首先流式传输到临时文件:
from shutil import copyfileobj
from tempfile import NamedTemporaryFile
with ZipFile("/a/certain/local/zip/file.zip", "w") as zip_file:
with NamedTemporaryFile() as cache:
with urlopen("http://a.certain.file/on?the=web") as response:
shutil.copyfileobj(response, cache)
cache.flush()
zipfile.write('an.entry', cache.name)
使用NamedTemporaryFile()
这样的方法仅适用于POSIX系统,在Windows上,您无法再次打开相同的文件名,因此您必须使用tempfile.mkstemp()
生成的名称,从那里打开文件,然后用于try...finally
清理.