我试图从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:不允许向后搜索”
我需要读取内容到mem,而不是转储到文件(extractall工作正常)
谢谢!
答案 0 :(得分:7)
问题在于这一行:
fd = tarfile.open('file.tar.gz', 'r|gz')
您不希望'r|gz'
,您需要'r:gz'
。
如果我在一个简单的tarball上运行你的代码,我甚至可以打印出member
并看到test/foo
,然后我会在read
上得到相同的错误。< / p>
如果我修复它以使用'r:gz'
,它就可以。
来自the docs:
模式必须是'filemode [:compression]'
形式的字符串...
出于特殊目的,模式还有第二种格式:'filemode | [compression]'。 tarfile.open()将返回一个TarFile对象,该对象将其数据作为块流进行处理。不会对文件进行随机搜索...将此变体与例如sys.stdin,套接字文件对象或磁带设备。但是,这样的TarFile对象受到限制,因为它不允许随机访问,请参阅示例。
'r|gz'
适用于具有不可搜索流的情况,并且仅提供操作的子集。不幸的是,它似乎没有准确记录允许哪些操作 - 并且到示例的链接没有帮助,因为没有一个示例使用此功能。因此,您必须阅读the source,或通过反复试验来解决问题。
但是,既然你有一个普通的,可搜索的文件,你就不用担心了;只需使用'r:gz'
。
答案 1 :(得分: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())