Удалить файл из zipfile с помощью модуля ZipFile - PullRequest
34 голосов
/ 05 февраля 2009

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

В Python 2.4 класс ZipInfo имел атрибут file_offset, поэтому можно было создать второй zip-файл и скопировать данные в другой файл без распаковки / повторного сжатия.

Это file_offset отсутствует в Python 2.6, поэтому есть ли другой вариант, кроме создания другого zip-файла путем распаковки каждого файла, а затем повторного сжатия его снова?

Возможно, есть прямой способ удаления файла в zip-файле, я искал и ничего не нашел.

Ответы [ 3 ]

40 голосов
/ 05 февраля 2009

У меня сработал следующий фрагмент (удаляет все файлы * .exe из архива Zip):

zin = zipfile.ZipFile ('archive.zip', 'r')
zout = zipfile.ZipFile ('archve_new.zip', 'w')
for item in zin.infolist():
    buffer = zin.read(item.filename)
    if (item.filename[-4:] != '.exe'):
        zout.writestr(item, buffer)
zout.close()
zin.close()

Если вы прочитаете все в память, вы можете исключить необходимость во втором файле. Однако этот фрагмент повторно сжимает все.

После более тщательной проверки ZipInfo.header_offset - это смещение от начала файла. Название вводит в заблуждение, но основной Zip-заголовок фактически хранится в конце файла. Мой hex-редактор подтверждает это.

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

Не изменяя основной заголовок, я получаю сообщение об ошибке «отсутствует X байт в zipfile» при его открытии. Этот может помочь вам узнать, как изменить основной заголовок.

4 голосов
/ 17 августа 2017

Не очень элегантно, но вот как я это сделал:

import subprocess
import zipfile

z = zipfile.ZipFile(zip_filename)

files_to_del = filter( lambda f: f.endswith('exe'), z.namelist()]

cmd=['zip', '-d', zip_filename] + files_to_del
subprocess.check_call(cmd)

# reload the modified archive
z = zipfile.ZipFile(zip_filename)
1 голос
/ 01 января 2017

Подпрограмма delete_from_zip_file из ruamel.std.zipfile ¹ позволяет удалить файл на основе его полного пути в ZIP или на основе (re) шаблонов. Например. Вы можете удалить все файлы .exe из test.zip, используя

from ruamel.std.zipfile import delete_from_zip_file

delete_from_zip_file('test.zip', pattern='.*.exe')  

(обратите внимание на точку перед *).

Это работает аналогично решению mdm (включая необходимость повторного сжатия), но воссоздает ZIP-файл в памяти (используя класс InMemZipFile()), перезаписывая старый файл после того, как он полностью прочитан.


¹ Отказ от ответственности: я являюсь автором этого пакета.

...