【问题标题】:How to write a large amount of data in a tarfile in python without using temporary file如何在不使用临时文件的情况下在 python 的 tarfile 中写入大量数据
【发布时间】: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。

有什么提示吗?

【问题讨论】:

    标签: python tar


    【解决方案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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多