dnagy dnagy - 2 months ago 8
Python Question

Python's zipfile module cannot update entries

I would like to update an entry in a zip file using pythons zipfile module.
My problem is that this generates a new entry.

Please assume I have this code:

from zipfile import ZipFile,ZIP_DEFLATED
with ZipFile("myfile.zip","w") as z:
z.writestr("hello.txt", "the content of hello.txt", ZIP_DEFLATED)
### how to update the hello.txt file here ?
z.writestr("hello.txt", "the content of hello.txt", ZIP_DEFLATED)


After this the actual zip file has two entries instead of one:

$ unzip -l myfile.zip
Archive: myfile.zip
Length Date Time Name
--------- ---------- ----- ----
24 2013-02-19 22:48 hello.txt
24 2013-02-19 22:48 hello.txt
--------- -------
48 2 files
$ python --version
Python 3.3.0
$


I know of the method to write a complete new file, but this would
take much time if the content is big.

The zip(1) utility can do this ( using the "-u" option) so why not python?
Is there any way I can still achieve this using python?

Thanks

Answer

The zip format doesn't have any easy way to delete or replace a file within the archive. There may be some library that can do so in-place, but I don't know of one.

But wait:

the zip(1) utitly can do this ( using the "-u" option) so why not python?

First, all -u does is tell it "only replace existing files if the timestamp isn't newer", which isn't really relevant here. Without the -u it will still, the default command is add, which does the same thing without checking the timestamps:

Update existing entries and add new files. If the archive does not exist create it. This is the default mode.

But, more importantly, as the manpage you referenced explicitly says:

Zip files. When changing an existing zip archive, zip will write a temporary file with the new contents, and only replace the old one when the process of creating the new version has been completed without error.

And that's exactly what you want to do: Write a complete new file to a temporary location, then replace the original with the new one.

This can be a bit painful if you need to make it work on Windows. (If not, just use tempfile.NamedTemporaryFile and os.rename.) But you already know how to do it:

I know of the method to write a complete new file, but this would take much time if the content is big.

No more so than zip -u takes too much time.