Как записать большой объем данных в tar-файл на python без использования временного файла

Я написал небольшой криптографический модуль на питоне, задачей которого является зашифровать файл и поместить результат в tar-файл. Исходный файл для шифрования может быть довольно большим, но это не проблема, потому что моей программе нужно работать только с небольшим блоком данных за раз, который можно зашифровать на лету и сохранить.

Я ищу способ избежать этого в два прохода, сначала записывая все данные во временный файл, а затем вставляя результат в tar-файл.

В основном я делаю следующее (где генератор_шифровальщик — это простой генератор, который выдает куски данных, считанные из исходного файла). :

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.

Любой намек на то, как это сделать?


person kriss    schedule 07.09.2009    source источник


Ответы (4)


Вы можете создать собственный файлоподобный объект и передать его в TarFile.addfile. Ваш файлоподобный объект будет генерировать зашифрованное содержимое на лету в методе fileobj.read().

person u0b34a0f6ae    schedule 07.09.2009
comment
Прочитайте tarfile.py в вашей библиотеке. Если я правильно понял, все, что объект файла должен реализовать, это .read() и .close(), и он будет работать. - person u0b34a0f6ae; 07.09.2009
comment
кажется достаточно легким. Я попробую этот пост, если он сработает. - person kriss; 07.09.2009
comment
единственное, что я вижу, что вам нужно решить, это то, что вы должны передать полный размер зашифрованного файла перед началом и вернуть фрагменты правильного размера, но я полагаю, вы можете повлиять на это. Также допустимо возвращать меньше запрошенного размера в .read(). - person u0b34a0f6ae; 07.09.2009
comment
если бы параллелизм было проще выразить на языках программирования, вам нужно было бы просто создать канал (os.pipe()), передать конец чтения в addfile и записать в конец ввода. Тем не менее, я думаю, что это ошибка сложности, поскольку вам нужно настроить разные потоки или процессы для чтения и записи. - person u0b34a0f6ae; 08.09.2009

Хм? Разве вы не можете просто использовать модуль subprocess для запуска канала в tar? Таким образом, временный файл не потребуется. Конечно, это не сработает, если вы не можете генерировать свои данные достаточно маленькими фрагментами, чтобы поместиться в ОЗУ, но если у вас есть такая проблема, то проблема не в tar.

person unwind    schedule 07.09.2009
comment
Все дело в том, чтобы избежать подпроцессов. Я хочу полное управление исключениями Python. Я не хочу разбирать stderr, чтобы узнать, почему tar не работает (в том числе из-за нехватки места на диске, не может открыть новый процесс и т. Д.). - person kriss; 07.09.2009

В основном использование файлового объекта и передача его в 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()
person Community    schedule 20.09.2009
comment
После просмотра исходного кода tarfile.py кажется достаточно простым изменить поведение, когда он ожидает, что чтение всегда возвращает полные буферы. Возможно залью как баг и предложу исправляющий патч. - person kriss; 21.09.2009
comment
ограничение на необходимость знать размер перед записью, вероятно, может быть изменено также, если базовый tar-файл открывается как реальный файл, по которому вы можете перемещаться (т. Е. Не поток, идущий всегда вперед). Это подразумевает только двойное написание заголовка tarfinfo, так как tarinfo записывается перед содержимым. Это также требует некоторых изменений в модуле tarfile (или в каком-то производном классе). - person kriss; 21.09.2009

Я думаю, вам нужно понять, как работает формат tar, и самостоятельно писать tar. Может быть, это может быть полезно?

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

person static_rtti    schedule 07.09.2009