Python: открыть zip-файл из двоичного файла вместо имени файла

Есть ли способ инициализировать объект ZipFile, передав буквальные байты zip-файла вместо того, чтобы читать имя файла? Я создаю спокойное приложение, которому не нужно прикасаться к диску; он просто открывает файл, что-то с ним делает, повторно архивирует и отправляет.


person limp_chimp    schedule 23.09.2013    source источник
comment
Просто для ясности: я хотел бы иметь возможность открывать zip-файл из двоичной строки. Например, я передаю строку, содержащую двоичные данные, и получаю объект ZipFile. Код StringIO, который я видел до сих пор, имел дело с записью ZipFile в строку, но, похоже, он не дает способа избежать записи временного файла.   -  person limp_chimp    schedule 23.09.2013


Ответы (3)


В комментариях к другим ответам вы говорите, что хотите сделать это:

открыть двоичную строку, как если бы это был zip-файл. Откройте его, прочитайте/запишите файлы внутри него, а затем закройте его.

Вы просто делаете то же самое, что и в других ответах, за исключением того, что вы создаете StringIO.StringIO (или cStringIO.StringIO или io.BytesIO), предварительно заполненный двоичной строкой, и извлекаете строку в конце. StringIO и его друзья берут необязательную начальную строку для своего конструктора и имеют метод getvalue для извлечения строки, когда вы закончите. Документация очень проста, и ее стоит прочитать.

Итак, придерживаясь как можно ближе к ответу Алексея:

from zipfile import ZipFile
try:
    import cStringIO as StringIO
except ImportError:
    import StringIO

in_memory = StringIO.StringIO(original_zip_data)
zf = ZipFile(in_memory, "a")  

zf.writestr("file.txt", "some text contents")

zf.close()

new_zip_data = in_memory.getvalue()

Однако обратите внимание, что ZipFile не может выполнять запись в zip-архив на месте, за исключением особого случая добавления к нему новых файлов. Это так же верно для zip-архивов в памяти, как и на диске. Часто вам может сойти с рук перезапись файла в архиве, добавив новый файл с тем же путем, но обычно это плохая идея (особенно если вы создаете эти вещи для отправки через Интернет).

Итак, то, что вы, вероятно, захотите сделать, точно такое же, как и при изменении файла: создайте отдельный выходной файл, скопируйте то, что вам нужно, из входного файла и напишите новые вещи по ходу дела. Просто в этом случае входной и выходной файлы являются ZipFile объектами, обертывающими StringIO объектов.

person abarnert    schedule 23.09.2013

Вот пример использования (c)StringIO:

from zipfile import ZipFile
try:
    import cStringIO as StringIO
except ImportError:
    import StringIO

in_memory = StringIO.StringIO()   
zf = ZipFile(in_memory, "a")  

zf.writestr("file.txt", "some text contents")

zf.close()

Также см:

person alecxe    schedule 23.09.2013
comment
Разве BytesIO не лучше? В частности, потому что тогда код будет переносим между 2 и 3. - person Marcin; 23.09.2013

Конечно, вместо этого используйте (c)StringIO: http://docs.python.org/2/library/stringio.html Кроме того, вам следует использовать BytesIO для Python 3. Однако он существует для версий 2.6 и 2.7.

person Nacib Neme    schedule 23.09.2013
comment
Я думаю, вы имеете в виду BytesIO, но да. - person Marcin; 23.09.2013
comment
@Marcin: В Python 2.x StringIO.StringIO нормально. В 3.x да, вместо этого вы бы хотели использовать io.BytesIO. - person abarnert; 23.09.2013
comment
@abarnert Верно. Таким образом, использование StringIO просто создает проблему переносимости без всякой причины. - person Marcin; 23.09.2013
comment
@Marcin: Конечно, но использование BytesIO также создает проблему переносимости, поскольку его нет в Python 2.5 (и, IIRC, значительно медленнее, чем StringIO/cStringIO до 2.7.2 или около того… но я мог ошибаться, и это вряд ли имеет значение). Иными словами: вы бы стали утверждать, что всегда следует использовать io.open в 2.x вместо open для обычных файлов? - person abarnert; 23.09.2013
comment
@abarnert На данный момент, в значительной степени да, если только не известно, что требуется совместимость до версии 2.7. Сейчас прошло 3 года с тех пор, как 2.7 является текущей серией выпусков. У кого-то очень мало причин использовать 2.6. (Я не считаю уважительной причиной наличие устаревшей версии, установленной вашим дистрибутивом). - person Marcin; 23.09.2013
comment
@Marcin: я полностью за то, чтобы подталкивать людей к использованию 3.3, когда это возможно, а в противном случае отказываться от совместимости с 2.5 и пытаться писать программное обеспечение с двойной версией, когда вам это может сойти с рук. Но если у вас, например, установлена ​​устаревшая версия дистрибутива, используемого вашей хостинговой компанией, вам это не сойдет с рук. Это все еще, к сожалению, реальность 2013 года. Таким образом, вы не можете просто сказать людям писать код, совместимый с 3.x, если они используют 2.x; вы должны объяснить различия. - person abarnert; 23.09.2013
comment
@Marcin: Кроме того, если вашему проекту в любом случае понадобится 2to3, часто более разумно писать код, совместимый с 2to3, чем код, совместимый напрямую с 3.x. - person abarnert; 23.09.2013
comment
@abarnert Python невелик. Я еще не пользовался услугами хостинга, где установка современного программного обеспечения была бы невозможна. Я также не думаю, что поддержка нескольких кодовых баз — замечательная идея. - person Marcin; 23.09.2013
comment
Это относится к одной половине того, что я ищу, но не к другой. Я хочу иметь возможность открывать двоичную строку, как если бы это был zip-файл. Откройте его, прочитайте/запишите файлы внутри него, а затем закройте его, даже не прикасаясь к диску. Может быть, я что-то пропустил, но я не думаю, что это покрывает это. - person limp_chimp; 23.09.2013
comment
@limp_chimp: Это тривиально: вы можете передать начальное значение конструктору StringIO/BytesIO, и вы можете получить окончательную строку в конце с помощью метода getvalue. Если это недостаточно ясно, я напишу ответ. - person abarnert; 23.09.2013