Удалить файл из zipfile с помощью модуля ZipFile
Единственный способ, которым я приступил к удалению файла из zip файла, - создать временный zip файл без файла, который нужно удалить, а затем переименуйте его в исходное имя файла.
В python 2.4 класс ZipInfo имел атрибут file_offset
, поэтому было возможно создать второй zip файл и скопировать данные в другой файл без распаковки/повторного сжатия.
Этот file_offset
отсутствует в python 2.6, так есть ли другой вариант, чем создание другого zip файла, распаковывая каждый файл, а затем повторно сбрасывая его?
Возможно, существует прямой способ удаления файла в zip файле, я искал и ничего не нашел.
Ответы
Ответ 1
Следующий фрагмент работал у меня (удаляет все *.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 байтов в zip файле", когда я его открываю. Это может помочь вам узнать, как изменить основной заголовок.
Ответ 2
Не очень элегантный, но вот как я это сделал:
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)
Ответ 3
Подпрограмма 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()
), перезаписывая старый файл после его полного чтения.
¹ Отказ от ответственности: я являюсь автором этого пакета.