如何在Python中写入大量数据到tar文件而不使用临时文件

3 投票
4 回答
4012 浏览
提问于 2025-04-15 14:08

我写了一个小的加密模块,使用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 个回答

2

基本上,使用一个类似文件的对象并把它传给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()
2

嗯?你难道不能直接用 subprocess 模块来把数据传给 tar 吗?这样就不需要临时文件了。当然,如果你的数据太大,无法分成小块放进内存,那这个方法就不行了,但如果你遇到这个问题,那 tar 也不是主要的原因。

4

你可以自己创建一个像文件一样的对象,然后把它传给 TarFile.addfile。这个像文件的对象会在 fileobj.read() 方法中实时生成加密的内容。

撰写回答