我在远程FTP服务器上有很多压缩文件,它们的大小高达20 TB。我只需要这些zip文档中的文件名,这样我就可以将它们插入Python脚本中。
有没有办法只获取文件名,而不实际下载文件并在我的本地机器上提取它们?如果是这样的话,有人能告诉我正确的库/包吗?
发布于 2018-12-04 17:42:43
您可以实现一个类似文件的对象,该对象从FTP读取数据,而不是本地文件。并将其传递给ZipFile
构造函数,而不是(本地)文件名。
一个简单的实现可以是:
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
print("seek {}".format(self.pos))
def tell(self):
print("tell {}".format(self.pos))
return self.pos
def read(self, size = None):
if size == None:
size = self.size - self.pos
print("read {}".format(size))
data = ""
# 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()
ftp.voidresp()
print("read {}".format(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
缓存中的所有呼叫。
知道了,你实际上可以做一个小hack。由于列表位于归档的末尾,因此您实际上只能下载归档的末尾。虽然下载的ZIP将被破坏,但仍然可以列出。这样,你就不需要FtpFile
了。
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
由于10 KB太小而无法包含整个列表而出现异常,则可以使用更大的块重试代码。
https://stackoverflow.com/questions/-100006212
复制相似问题