将类文件对象传递给另一个类文件对象的 write() 方法
Passing a file-like object to write() method of another file-like object
我正在尝试从网络上获取一个大文件,并将其直接流式传输到 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 reader。但是,由于文件相当大,我不想在压缩之前将整个文件加载到 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 管道一样?
您可以使用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()
,然后将该块传递给目标文件对象上的 .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()
generated name,从那里打开文件,然后使用 try...finally
进行清理。
我正在尝试从网络上获取一个大文件,并将其直接流式传输到 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 reader。但是,由于文件相当大,我不想在压缩之前将整个文件加载到 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 管道一样?
您可以使用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()
,然后将该块传递给目标文件对象上的 .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()
generated name,从那里打开文件,然后使用 try...finally
进行清理。