如何在不下载的情况下在 AWS S3 中列出 tar 中的文件?

par*_*y72 7 tar amazon-s3 python-3.x boto3

在四处寻找想法时,我发现/sf/answers/3795571321/用于 zip 文件,我认为这是一个非常聪明的解决方案。但它依赖于具有中央目录的zip 文件- tar 文件没有。

我想我可以遵循相同的一般原则,并通过参数将 S3 文件公开给tarfilefileobj

import boto3
import io
import tarfile

class S3File(io.BytesIO):
    def __init__(self, bucket_name, key_name, s3client):
        super().__init__()
        self.bucket_name = bucket_name
        self.key_name = key_name
        self.s3client = s3client
        self.offset = 0

    def close(self):
        return

    def read(self, size):
        print('read: offset = {}, size = {}'.format(self.offset, size))
        start = self.offset
        end = self.offset + size - 1
        try:
            s3_object = self.s3client.get_object(Bucket=self.bucket_name, Key=self.key_name, Range="bytes=%d-%d" % (start, end))
        except:
            return bytearray()
        self.offset = self.offset + size
        result = s3_object['Body'].read()
        return result

    def seek(self, offset, whence=0):
        if whence == 0:
            print('seek: offset {} -> {}'.format(self.offset, offset))
            self.offset = offset

    def tell(self):
        return self.offset

s3file = S3File(bucket_name, file_name, s3client)
tarf = tarfile.open(fileobj=s3file)
names = tarf.getnames()
for name in names:
    print(name)
Run Code Online (Sandbox Code Playgroud)

这工作正常,除了输出看起来像:

read: offset = 0, size = 2
read: offset = 2, size = 8
read: offset = 10, size = 8192
read: offset = 8202, size = 1235
read: offset = 9437, size = 1563
read: offset = 11000, size = 3286
read: offset = 14286, size = 519
read: offset = 14805, size = 625
read: offset = 15430, size = 1128
read: offset = 16558, size = 519
read: offset = 17077, size = 573
read: offset = 17650, size = 620
(continued)
Run Code Online (Sandbox Code Playgroud)

tarfile 无论如何都只是读取整个文件,所以我什么也没得到。有没有让 tarfile 只读取它需要的文件部分?我能想到的唯一替代方法是重新实现 tar 文件解析,因此它:

  1. 读取 512 字节的标头并将其写入BytesIO缓冲区。
  2. 获取后续文件的大小并将零写入BytesIO缓冲区。
  3. 跳过文件到下一个标题。

但这似乎过于复杂。

par*_*y72 7

我的错。我实际上正在处理 tar.gz 文件,但我认为 zip 和 tar.gz 是相似的。它们不是 - tar 是一个存档文件,然后被压缩为 gzip,因此要读取 tar,您必须先将其解压缩。我从 tar 文件中提取位的想法行不通。

起作用的是:

s3_object = s3client.get_object(Bucket=bucket_name, Key=file_name)
wholefile = s3_object['Body'].read()
fileobj = io.BytesIO(wholefile)
tarf = tarfile.open(fileobj=fileobj)
names = tarf.getnames()
for name in names:
    print(name)
Run Code Online (Sandbox Code Playgroud)

我怀疑原始代码适用于 tar 文件,但我没有任何代码可以尝试。