如何在Python中写入大量数据到tar文件而不使用临时文件
我写了一个小的加密模块,使用Python编程,它的任务是对一个文件进行加密,然后把结果放到一个tar文件里。要加密的原始文件可能会很大,但这没关系,因为我的程序只需要一次处理一小块数据,这样就可以边加密边存储。
我想找一种方法,避免先把所有数据写入一个临时文件,然后再把结果放入tar文件中。
基本上,我的做法是这样的(其中generator_encryptor是一个简单的生成器,它从源文件中读取数据块并逐个输出):
t = tarfile.open("target.tar", "w")
tmp = file('content', 'wb')
for chunk in generator_encryptor("sourcefile"):
tmp.write(chunks)
tmp.close()
t.add(content)
t.close()
我有点烦恼,因为我必须使用一个临时文件。我觉得直接把数据块写入tar文件应该是很简单的,但把每个数据块收集到一个字符串中,然后用类似t.addfile('content', StringIO(bigcipheredstring)的方式似乎不太可行,因为我不能保证我有足够的内存来存储bigcipheredstring。
有没有什么建议可以做到这一点?
4 个回答
基本上,使用一个类似文件的对象并把它传给TarFile.addfile就可以解决问题,但还有一些未解决的事项。
- 我需要在一开始就知道完整的加密文件大小。
- tarfile读取方法的访问方式是这样的:自定义的类似文件的对象必须总是返回完整的读取缓冲区,否则tarfile会认为已经到文件末尾。这导致了读取方法中的一些非常低效的缓冲区复制,但要么这样,要么就得改动tarfile模块。
下面是最终的代码,基本上我不得不写一个包装类,把我现有的生成器转换成一个类似文件的对象。我在我的例子中还添加了GeneratorEncrypto类,以使代码完整。你会注意到它有一个len方法,可以返回写入文件的长度(但要明白这只是一个虚拟的占位符,实际上没有做什么有用的事情)。
import tarfile
class GeneratorEncryptor(object):
"""Dummy class for testing purpose
The real one perform on the fly encryption of source file
"""
def __init__(self, source):
self.source = source
self.BLOCKSIZE = 1024
self.NBBLOCKS = 1000
def __call__(self):
for c in range(0, self.NBBLOCKS):
yield self.BLOCKSIZE * str(c%10)
def __len__(self):
return self.BLOCKSIZE * self.NBBLOCKS
class GeneratorToFile(object):
"""Transform a data generator into a conventional file handle
"""
def __init__(self, generator):
self.buf = ''
self.generator = generator()
def read(self, size):
chunk = self.buf
while len(chunk) < size:
try:
chunk = chunk + self.generator.next()
except StopIteration:
self.buf = ''
return chunk
self.buf = chunk[size:]
return chunk[:size]
t = tarfile.open("target.tar", "w")
tmp = file('content', 'wb')
generator = GeneratorEncryptor("source")
ti = t.gettarinfo(name = "content")
ti.size = len(generator)
t.addfile(ti, fileobj = GeneratorToFile(generator))
t.close()
嗯?你难道不能直接用 subprocess 模块来把数据传给 tar 吗?这样就不需要临时文件了。当然,如果你的数据太大,无法分成小块放进内存,那这个方法就不行了,但如果你遇到这个问题,那 tar 也不是主要的原因。
你可以自己创建一个像文件一样的对象,然后把它传给 TarFile.addfile
。这个像文件的对象会在 fileobj.read()
方法中实时生成加密的内容。