) идентифицировать файлы и удалить их при необходимости.

я естьarchive.zip с двумя файлами:hello.txt а такжеworld.txt

Я хочу перезаписатьhello.txt файл с новым с этим кодом:

import zipfile

z = zipfile.ZipFile('archive.zip','a')
z.write('hello.txt')
z.close()  

но это не будет перезаписывать файл, так или иначе, он создает другой экземплярhello.txt - взгляните на скриншот winzip:

Так как нет ничего подобногоzipfile.remove()Какой лучший способ справиться с этой проблемой?

 denfromufa25 мая 2016 г., 18:50
открытый вопрос:bugs.python.org/issue6818

Ответы на вопрос(2)

Решение Вопроса

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

Ниже приведен код для этого. Но обратите внимание, что он неэффективен, поскольку распаковывает, а затем повторно сжимает все данные.

import tempfile
import zipfile
import shutil
import os

def remove_from_zip(zipfname, *filenames):
    tempdir = tempfile.mkdtemp()
    try:
        tempname = os.path.join(tempdir, 'new.zip')
        with zipfile.ZipFile(zipfname, 'r') as zipread:
            with zipfile.ZipFile(tempname, 'w') as zipwrite:
                for item in zipread.infolist():
                    if item.filename not in filenames:
                        data = zipread.read(item.filename)
                        zipwrite.writestr(item, data)
        shutil.move(tempname, zipfname)
    finally:
        shutil.rmtree(tempdir)

Использование:

remove_from_zip('archive.zip', 'hello.txt')
with zipfile.ZipFile('archive.zip', 'a') as z:
    z.write('hello.txt')
 nosklo11 янв. 2011 г., 04:33
@ cru3l: Это именно то, что я говорю в своем ответе.
 nukl11 янв. 2011 г., 04:32
Итак, нет никакого эффективного способа перезаписи файлов вообще? Может быть, другой почтовый модуль? Во всяком случае, спасибо за это
 Apalala11 янв. 2011 г., 15:51
Вы можете вызвать внешний почтовый инструмент. Кроме того, вы можете создать свой собственный интерфейс к библиотеке zip.

который наследуется от ZipFile, поддерживает тот же интерфейс, но добавляет возможность перезаписи файлов (через writestr или write) и удаления файлов.

import os
import shutil
import tempfile
from zipfile import ZipFile, ZIP_STORED, ZipInfo


class UpdateableZipFile(ZipFile):
    """
    Add delete (via remove_file) and update (via writestr and write methods)
    To enable update features use UpdateableZipFile with the 'with statement',
    Upon  __exit__ (if updates were applied) a new zip file will override the exiting one with the updates
    """

    class DeleteMarker(object):
        pass

    def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=False):
        # Init base
        super(UpdateableZipFile, self).__init__(file, mode=mode,
                                                compression=compression,
                                                allowZip64=allowZip64)
        # track file to override in zip
        self._replace = {}
        # Whether the with statement was called
        self._allow_updates = False

    def writestr(self, zinfo_or_arcname, bytes, compress_type=None):
        if isinstance(zinfo_or_arcname, ZipInfo):
            name = zinfo_or_arcname.filename
        else:
            name = zinfo_or_arcname
        # If the file exits, and needs to be overridden,
        # mark the entry, and create a temp-file for it
        # we allow this only if the with statement is used
        if self._allow_updates and name in self.namelist():
            temp_file = self._replace[name] = self._replace.get(name,
                                                                tempfile.TemporaryFile())
            temp_file.write(bytes)
        # Otherwise just act normally
        else:
            super(UpdateableZipFile, self).writestr(zinfo_or_arcname,
                                                    bytes, compress_type=compress_type)

    def write(self, filename, arcname=None, compress_type=None):
        arcname = arcname or filename
        # If the file exits, and needs to be overridden,
        # mark the entry, and create a temp-file for it
        # we allow this only if the with statement is used
        if self._allow_updates and arcname in self.namelist():
            temp_file = self._replace[arcname] = self._replace.get(arcname,
                                                                   tempfile.TemporaryFile())
            with open(filename, "rb") as source:
                shutil.copyfileobj(source, temp_file)
        # Otherwise just act normally
        else:
            super(UpdateableZipFile, self).write(filename, 
                                                 arcname=arcname, compress_type=compress_type)

    def __enter__(self):
        # Allow updates
        self._allow_updates = True
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # call base to close zip file, organically
        try:
            super(UpdateableZipFile, self).__exit__(exc_type, exc_val, exc_tb)
            if len(self._replace) > 0:
                self._rebuild_zip()
        finally:
            # In case rebuild zip failed,
            # be sure to still release all the temp files
            self._close_all_temp_files()
            self._allow_updates = False

    def _close_all_temp_files(self):
        for temp_file in self._replace.itervalues():
            if hasattr(temp_file, 'close'):
                temp_file.close()

    def remove_file(self, path):
        self._replace[path] = self.DeleteMarker()

    def _rebuild_zip(self):
        tempdir = tempfile.mkdtemp()
        try:
            temp_zip_path = os.path.join(tempdir, 'new.zip')
            with ZipFile(self.filename, 'r') as zip_read:
                # Create new zip with assigned properties
                with ZipFile(temp_zip_path, 'w', compression=self.compression,
                             allowZip64=self._allowZip64) as zip_write:
                    for item in zip_read.infolist():
                        # Check if the file should be replaced / or deleted
                        replacement = self._replace.get(item.filename, None)
                        # If marked for deletion, do not copy file to new zipfile
                        if isinstance(replacement, self.DeleteMarker):
                            del self._replace[item.filename]
                            continue
                        # If marked for replacement, copy temp_file, instead of old file
                        elif replacement is not None:
                            del self._replace[item.filename]
                            # Write replacement to archive,
                            # and then close it (deleting the temp file)
                            replacement.seek(0)
                            data = replacement.read()
                            replacement.close()
                        else:
                            data = zip_read.read(item.filename)
                        zip_write.writestr(item, data)
            # Override the archive with the updated one
            shutil.move(temp_zip_path, self.filename)
        finally:
            shutil.rmtree(tempdir)

пример использования:

with UpdateableZipFile("C:\Temp\Test2.docx", "a") as o:
    # Overwrite a file with a string
    o.writestr("word/document.xml", "Some data")
    # exclude an exiting file from the zip
    o.remove_file("word/fontTable.xml")
    # Write a new file (with no conflict) to the zp
    o.writestr("new_file", "more data")
    # Overwrite a file with a file
    o.write(r"C:\Temp\example.png", "word/settings.xml")
 Jezor22 июн. 2016 г., 17:58
Когда я использую класс UpdateableZipFile, как в вашем примере, возникают два исключения:File "/home/jezor/Code/updateable_zipfile.py", line 38, in writestr temp_file.write(bytes) TypeError: a bytes-like object is required, not 'str' а такжеFile "/home/jezor/Code/updateable_zipfile.py", line 77, in _close_all_temp_files for temp_file in self._replace.itervalues(): AttributeError: 'dict' object has no attribute 'itervalues', Не могли бы вы обновить свой ответ? Я использую Python 3.4 и этот класс является лучшим решением дляэтот вопрос.
 Or Weis19 апр. 2018 г., 10:40
@morsik Вы, вероятно, открываете существующие файлы, которые не являются zip-файлами или повреждены. Попробуйте использовать предложение Try-Catch (stackoverflow.com/questions/39154470/...) идентифицировать файлы и удалить их при необходимости.
 morsik17 апр. 2018 г., 14:02
К сожалению, я получаю многоBadZipfile: File is not a zip file ошибки даже при создании нового файла ZIP. Py2.7
 VectorVictor04 окт. 2017 г., 13:24
Я не был уверен в коде, учитывая небольшое количество голосов, но он работает очень хорошо, кажется хорошо закодированным и соответствует требованиям этого и нескольких повторяющихся вопросов.
 Mad Physicist24 сент. 2016 г., 05:55
@Jezor. Вы пытаетесь запустить код Py2 в Py3, следовательно, ошибка. + Изменитьitervalues чтобы простоvalues.

Ваш ответ на вопрос