3

我试图从 tar.gz 文件中只读取一个文件。对 tarfile 对象的所有操作都可以正常工作,但是当我从具体成员中读取时,总是会引发 StreamError,请检查以下代码:

import tarfile
fd = tarfile.open('file.tar.gz', 'r|gz')
for member in fd.getmembers():
    if not member.isfile():
        continue
    cfile = fd.extractfile(member)
    print cfile.read()
    cfile.close()
fd.close()

cfile.read() 总是导致“tarfile.StreamError:不允许向后搜索”

我需要将内容读取到内存,而不是转储到文件(extractall 工作正常)

谢谢!

4

2 回答 2

10

问题是这一行:

fd = tarfile.open('file.tar.gz', 'r|gz')

你不想要'r|gz',你想要'r:gz'

如果我在一个简单的 tarball 上运行你的代码,我什至可以打印出member并查看test/foo,然后我会得到与read你得到的相同的错误。

如果我修复它使用'r:gz',它的工作原理。

文档

mode 必须是格式为 'filemode[:compression]' 的字符串

...

出于特殊目的,模式有第二种格式:'filemode|[compression]'。tarfile.open() 将返回一个 TarFile 对象,该对象将其数据作为块流处理。不会对文件进行随机搜索...将此变体与例如 sys.stdin、套接字文件对象或磁带设备结合使用。但是,这样的 TarFile 对象是有限制的,因为它不允许被随机访问,请参阅示例。

'r|gz'适用于当您有不可搜索的流时,它只提供操作的子集。不幸的是,它似乎没有准确记录哪些操作是允许的——并且示例的链接也无济于事,因为没有一个示例使用此功能。因此,您必须阅读源代码,或者通过反复试验找出答案。

但是,由于您有一个正常的、可搜索的文件,您不必担心这一点;只需使用'r:gz'.

于 2013-02-08T10:36:22.887 回答
0

除了文件模式外,我还尝试seek了网络流。

我在尝试requests.get文件时遇到了同样的错误,所以我将所有内容提取到一个 tmp 目录:

# stream == requests.get
inputs = [tarfile.open(fileobj=LZMAFile(stream), mode='r|')]
t = "/tmp"
for tarfileobj in inputs:        
    tarfileobj.extractall(path=t, members=None)
for fn in os.listdir(t):
    with open(os.path.join(t, fn)) as payload:
        print(payload.read())
于 2016-12-17T02:48:13.807 回答