如何在不使用临时文件的情况下在 python 的 tarfile 中写入大量数据

Posted

技术标签:

【中文标题】如何在不使用临时文件的情况下在 python 的 tarfile 中写入大量数据【英文标题】:How to write a large amount of data in a tarfile in python without using temporary file 【发布时间】:2009-09-07 14:33:56 【问题描述】:

我在 python 中编写了一个小型加密模块,其任务是加密文件并将结果放入 tar 文件中。要加密的原始文件可以很大,但这不是问题,因为我的程序一次只需要处理一小块数据,可以即时加密并存储。

我正在寻找一种避免分两次执行的方法,首先将所有数据写入临时文件,然后将结果插入 tarfile。

基本上我会执行以下操作(其中 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。

有什么提示吗?

【问题讨论】:

【参考方案1】:

您可以创建自己的类似文件的对象并传递给 TarFile.addfile。您的类文件对象将在 fileobj.read() 方法中动态生成加密内容。

【讨论】:

读入库中的 tarfile.py。如果我没看错的话,所有文件对象必须实现的是 .read() 和 .close() 并且它会起作用。 似乎很容易。如果它有效,我会尝试返回一个帖子列表。 我看到你必须解决的唯一事情是你必须在开始之前传递完整的加密文件大小,并返回正确大小的块,但我想你可以影响它。在 .read() 中返回小于请求的大小也是有效的。 如果并发在编程语言中更容易表达,您可能只需要创建一个管道(os.pipe()),将读取端传递给 addfile,然后写入输入端。但是,我认为这是一个复杂性失败,因为您必须设置不同的线程或进程来读取和写入。【参考方案2】:

嗯?你不能只使用subprocess 模块来运行管道到tar 吗?这样,就不需要临时文件。当然,如果您无法生成足够小的数据块以放入 RAM 中,这将不起作用,但如果您有这个问题,那么 tar 不是问题。

【讨论】:

重点是避免子进程。我想要完整的 python 异常管理。我不想通过解析 stderr 来了解 tar 失败的原因(包括磁盘空间不足、无法打开新进程等)。【参考方案3】:

基本上使用类似文件的对象并将其传递给 TarFile.addfile 就可以了,但仍然存在一些问题。

一开始我需要知道完整的加密文件大小 tarfile 访问 read 方法的方式是,自定义类文件对象必须始终返回完整的读取缓冲区,或者 tarfile 假设它是文件结尾。这会导致 read 方法的代码中的一些非常低效的缓冲区复制,但要么就是这样,要么更改 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()

【讨论】:

在查看 tarfile.py 源代码之后,似乎很容易改变它期望读取总是返回完整缓冲区的行为。我可能会将它作为一个错误填充并提出一个更正补丁。 如果底层的 tarfile 作为一个可以移动的真实文件打开,那么在写入之前必须知道大小的限制也可能会被更改(即:不是一个始终向前的流)。它只意味着写入两次 tarfinfo 标头,因为 tarinfo 是在内容之前写入的。它还需要对 tarfile 模块(或某些派生类)进行一些更改。【参考方案4】:

我想您需要了解 tar 格式的工作原理,并自己处理 tar 编写。也许这会有所帮助?

http://mail.python.org/pipermail/python-list/2001-August/100796.html

【讨论】:

以上是关于如何在不使用临时文件的情况下在 python 的 tarfile 中写入大量数据的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用临时表或视图的情况下在多个列上使用 PIVOT [重复]

如何在不使用转储的情况下在 python 中编写 json 文件

如何在不脱离控制台的情况下在 Windows 中的 Python 中执行 os.execv()?

如何在不传递引用的情况下在 Python 中使用 SyncManager 跨进程共享列表

如何在不安装的情况下在应用程序中使用 Berkeley DB

如何在不改变其位置的情况下在界面生成器中将子视图置于前面