我在远程FTP服务器上有很多zip存档,它们的大小高达20TB。我只需要这些zip归档文件中的文件名,以便可以将它们插入我的Python脚本中。
有什么方法可以获取文件名而无需实际下载文件并将其解压缩到本地计算机上吗?如果是这样,有人可以将我定向到正确的库/软件包吗?
答案 0 :(得分:3)
您可以实现一个类似文件的对象,该对象从FTP读取数据,而不是本地文件。并将其(而不是(本地)文件名)传递给ZipFile
constructor。
一个简单的实现可以像:
from ftplib import FTP
from ssl import SSLSocket
class FtpFile:
def __init__(self, ftp, name):
self.ftp = ftp
self.name = name
self.size = ftp.size(name)
self.pos = 0
def seek(self, offset, whence):
if whence == 0:
self.pos = offset
if whence == 1:
self.pos += offset
if whence == 2:
self.pos = self.size + offset
def tell(self):
return self.pos
def read(self, size = None):
if size == None:
size = self.size - self.pos
data = B""
# based on FTP.retrbinary
# (but allows stopping after certain number of bytes read)
ftp.voidcmd('TYPE I')
cmd = "RETR {}".format(self.name)
conn = ftp.transfercmd(cmd, self.pos)
try:
while len(data) < size:
buf = conn.recv(min(size - len(data), 8192))
if not buf:
break
data += buf
# shutdown ssl layer (can be removed if not using TLS/SSL)
if SSLSocket is not None and isinstance(conn, SSLSocket):
conn.unwrap()
finally:
conn.close()
try:
ftp.voidresp()
except:
pass
self.pos += len(data)
return data
然后您可以像使用它一样
ftp = FTP(host, user, passwd)
ftp.cwd(path)
ftpfile = FtpFile(ftp, "archive.zip")
zip = zipfile.ZipFile(ftpfile)
print(zip.namelist())
以上实现是相当琐碎且效率低下的。它开始下载小块数据(至少三个),以检索包含文件的列表。可以通过读取和缓存更大的块来对其进行优化。但这应该可以给您带来想法。
特别是,您可以利用仅阅读清单的事实。该列表位于ZIP归档文件的和。因此,您一开始就可以下载最后一个(约)10 KB的数据。这样,您就可以完成该缓存中的所有read
调用。
知道这一点,您实际上可以进行一些小小的改动。由于清单位于存档的末尾,因此您实际上只能下载存档的末尾。虽然下载的ZIP将被破坏,但仍可以列出。这样,您将不需要FtpFile
类。您可以even download the listing to memory(StringIO
)。
zipstring = StringIO()
name = "archive.zip"
size = ftp.size(name)
ftp.retrbinary("RETR " + name, zipstring.write, rest = size - 10*2024)
zip = zipfile.ZipFile(zipstring)
print(zip.namelist())
如果由于10 KB太小而无法包含整个列表,则出现BadZipfile
异常,则可以重试较大的代码。