2010-03-17 3 views
35

Есть ли библиотека Python, которая позволяет манипулировать zip-архивами в памяти без необходимости использования реальных файлов на диске?Python in-memory zip library

Библиотека ZipFile не позволяет вам обновлять архив. Единственный способ состоит в том, чтобы извлечь его в каталог, внести изменения и создать новый ZIP из этого каталога. Я хочу изменить zip-архивы без доступа к диску, потому что я буду загружать их, вносить изменения и загружать их снова, поэтому у меня нет причин их хранить.

Нечто похожее на ZipInputStream/ZipOutputStream от Java могло бы сделать трюк, хотя любой интерфейс вообще позволяет избежать доступа к диску.

+1

Хорошо, я вижу, что я был неправ о ZipFile в конце концов. Всем спасибо. –

ответ

54

Согласно Python docs:

class zipfile.ZipFile(file[, mode[, compression[, allowZip64]]]) 

    Open a ZIP file, where file can be either a path to a file (a string) or a file-like object. 

Итак, чтобы открыть файл в памяти, просто создать файл-подобный объект (возможно, с помощью BytesIO).

file_like_object = io.BytesIO(my_zip_data) 
zipfile_ob = zipfile.ZipFile(file_like_object) 
+1

круто спасибо. Хотя, я думаю, вы хотите, чтобы последняя переменная называлась чем-то другим, например, 'the_zip_file'. В противном случае это не будет компилироваться – Kirby

+0

@Kirby Вы правы. Этот код работал бы, если бы он был в глобалях скрипта, но в функции «zipfile» был бы только локальным. Хороший улов. –

+0

@ JasonR.Coombs: Это будет работать как-то, но на самом деле. Даже в глобальной части скрипта он перезаписывает модуль «zipfile», что делает невозможным использование модуля «zipfile» позже в скрипте. Таким образом, это будет работать, только если вы обрабатываете только один zip-файл в глобальной части. – vog

31

Из статьи In-Memory Zip in Python:

Ниже пост шахты с мая 2008 года по проносясь в памяти с Python, повторно размещен так Posterous завершает работу.

Недавно я заметил, что для zip-файлов в памяти с Python имеется компонент для оплаты. Учитывая, что это то, что должно быть бесплатным, я собрал следующий код. Он прошел только через очень базовое тестирование, поэтому, если кто-либо обнаружит какие-либо ошибки, сообщите мне, и я обновлю это.

import zipfile 
import StringIO 

class InMemoryZip(object): 
    def __init__(self): 
     # Create the in-memory file-like object 
     self.in_memory_zip = StringIO.StringIO() 

    def append(self, filename_in_zip, file_contents): 
     '''Appends a file with name filename_in_zip and contents of 
     file_contents to the in-memory zip.''' 
     # Get a handle to the in-memory zip in append mode 
     zf = zipfile.ZipFile(self.in_memory_zip, "a", zipfile.ZIP_DEFLATED, False) 

     # Write the file to the in-memory zip 
     zf.writestr(filename_in_zip, file_contents) 

     # Mark the files as having been created on Windows so that 
     # Unix permissions are not inferred as 0000 
     for zfile in zf.filelist: 
      zfile.create_system = 0   

     return self 

    def read(self): 
     '''Returns a string with the contents of the in-memory zip.''' 
     self.in_memory_zip.seek(0) 
     return self.in_memory_zip.read() 

    def writetofile(self, filename): 
     '''Writes the in-memory zip to a file.''' 
     f = file(filename, "w") 
     f.write(self.read()) 
     f.close() 

if __name__ == "__main__": 
    # Run a test 
    imz = InMemoryZip() 
    imz.append("test.txt", "Another test").append("test2.txt", "Still another") 
    imz.writetofile("test.zip") 
+0

Полезная ссылка - это хороший пример использования объекта ZipFile способом, описанным в ответе Джейсона. Thanks –

+0

Без проблем, рад, что вы сочли это полезным. –

+0

Упоминайте содержание ссылки здесь, если оно умирает, так и ваш ответ –

14

Пример Этье предоставленном несколько проблем, некоторые из них главным:

  • не работает для реальных данных на Windows. ZIP-файл является двоичным, и его данные всегда должны быть записаны с открытым файлом 'wb'
  • ZIP-файл добавляется к каждому файлу, это неэффективно. Его можно просто открыть и сохранить как атрибут InMemoryZip
  • В документации указано, что ZIP-файлы должны быть закрыты явно, это не выполняется в функции добавления (это, вероятно, работает (для примера), потому что zf выходит за рамки и что закрывает ZIP-файл)
  • флаг create_system устанавливается для всех файлов в zip-файле каждый время, когда файл добавляется вместо одного раза в файл.
  • на Python < 3 cStringIO является гораздо более эффективным, чем StringIO
  • не работает на Python 3 (оригинал статьи от до версии 3.0, но к тому времени код был отправлен 3,1 был за долгое время).

Обновленная версия доступна, если вы устанавливаете ruamel.std.zipfile (из которых я являюсь автором).После

pip install ruamel.std.zipfile 

или включая код класса из here, вы можете сделать:

import ruamel.std.zipfile as zipfile 

# Run a test 
zipfile.InMemoryZipFile() 
imz.append("test.txt", "Another test").append("test2.txt", "Still another") 
imz.writetofile("test.zip") 

Вы можете альтернативно записать содержимое с помощью imz.data в любом месте вам нужно.

Вы также можете использовать with заявление, и если вы предоставите имя файла, содержимое ZIP будет записано на выходе, что контекст:

with zipfile.InMemoryZipFile('test.zip') as imz: 
    imz.append("test.txt", "Another test").append("test2.txt", "Still another") 

из-за задержки при записи на диск, вы можете на самом деле прочитанный из старого test.zip в этом контексте.

+0

Почему бы не использовать io.BytesIO в python 2? – boxed

+0

@boxed Нет особой причины, кроме того, что вы должны проверить, использует ли BytesIO 2,7 гораздо более быструю базовую реализацию C и не является только уровнем совместимости Python, вызывающим StringIO (вместо CStringIO) – Anthon

2

ПИТОН 3

import io 
import zipfile 

zip_buffer = io.BytesIO() 
with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED, False) as zip_file: 
    for file_name, data in [('1.txt', io.BytesIO(b'111')), ('2.txt', io.BytesIO(b'222'))]: 
     zip_file.writestr(file_name, data.getvalue()) 
with open('C:/1.zip', 'wb') as f: 
    f.write(zip_buffer.getvalue())