我有很多zip文件存储在远程FTP服务器上,它们的大小高达20TB。我只需要这些zip文件内部的文件名,以便将它们插入到我的Python脚本中。
有没有办法仅获取文件名而不实际下载文件并在本地机器上提取它们?如果有,有人可以指导我使用正确的库/包吗?
ZipFile
构造函数,而不是传递本地文件名。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)
# An alternative implementation is at
# https://stackoverflow.com/q/58819210/850848#58819362
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
类了。你甚至可以将列表下载到内存(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())
BadZipfile
异常,无法包含整个列表,则可以使用更大的块重新尝试该代码。