Python 2.6不喜欢附加到zip文件中的现有存档

时间:2010-05-27 11:50:49

标签: python zipfile

在我正在研究的程序的一些Python单元测试中,我们使用内存中的zipfiles进行端到端测试。在SetUp()中我们创建了一个简单的zip文件,但在某些测试中我们想要覆盖一些档案。为此,我们执行“zip.writestr(archive_name,zip.read(archive_name)+ new_content)”。像

这样的东西
import zipfile
from StringIO import StringIO

def Foo():
    zfile = StringIO()
    zip = zipfile.ZipFile(zfile, 'a')
    zip.writestr(
        "foo",
        "foo content")
    zip.writestr(
        "bar",
        "bar content")
    zip.writestr(
        "foo",
        zip.read("foo") +
        "some more foo content")
    print zip.read("bar")

Foo()

问题是这在Python 2.4和2.5中运行良好,但不是 2.6。在Python 2.6中,打印行上的“BadZipfile:目录中的文件名”栏“和标题”foo“不同。”

它似乎正在读取正确的文件栏,但它认为它应该是读取foo。

我很茫然。我究竟做错了什么?这不受支持吗?我尝试在网上搜索,但没有提到类似的问题。我读了zipfile文档,但找不到任何(我认为是)相关的文件,特别是因为我用文件名字符串调用read()。

有什么想法吗?

提前谢谢!

2 个答案:

答案 0 :(得分:2)

PKZIP文件是高度结构化的,只是附加到最后将搞砸了。我不能说早期版本的工作,但这个问题的解决方法是打开一个zipfile进行阅读,打开一个新的写入,提取第一个的内容,然后在最后添加你的添加组件。完成后,用新创建的zipfile替换原始zipfile。

运行代码时运行代码时得到的回溯是:

Traceback (most recent call last):
  File "zip.py", line 19, in <module>
    Foo()
  File "zip.py", line 17, in Foo
    print zip.read("bar")
  File "/usr/lib/python2.6/zipfile.py", line 834, in read
    return self.open(name, "r", pwd).read()
  File "/usr/lib/python2.6/zipfile.py", line 874, in open
    zinfo.orig_filename, fname)
zipfile.BadZipfile: File name in directory "bar" and header "foo" differ.

仔细观察后,我注意到您正在读取一个类似文件的StringIO,它以'a'ppend模式打开,这会导致读取错误,因为'a'通常不可读,当然必须是seek()ed读写之间。我会愚弄一些并更新它。

<强>更新

从Doug Hellmann的优秀Python Module of the Week中窃取了几乎所有这些代码后,我发现它的工作方式与我预期的相当。人们不能仅仅附加到结构化的PKZIP文件中,如果原始帖子中的代码确实有效,那就是偶然的:

import zipfile
import datetime

def create(archive_name):
    print 'creating archive'
    zf = zipfile.ZipFile(archive_name, mode='w')
    try:
        zf.write('/etc/services', arcname='services')
    finally:
        zf.close()

def print_info(archive_name):
    zf = zipfile.ZipFile(archive_name)
    for info in zf.infolist():
        print info.filename
        print '\tComment:\t', info.comment
        print '\tModified:\t', datetime.datetime(*info.date_time)
        print '\tSystem:\t\t', info.create_system, '(0 = Windows, 3 = Unix)'
        print '\tZIP version:\t', info.create_version
        print '\tCompressed:\t', info.compress_size, 'bytes'
        print '\tUncompressed:\t', info.file_size, 'bytes'
        print
    zf.close()

def append(archive_name):
    print 'appending archive'
    zf = zipfile.ZipFile(archive_name, mode='a')
    try:
        zf.write('/etc/hosts', arcname='hosts')
    finally:
        zf.close()

def expand_hosts(archive_name):
    print 'expanding hosts'
    zf = zipfile.ZipFile(archive_name, mode='r')
    try:
        host_contents = zf.read('hosts')
    finally:
        zf.close

    zf =  zipfile.ZipFile(archive_name, mode='a')
    try:
        zf.writestr('hosts', host_contents + '\n# hi mom!')
    finally:
        zf.close()

def main():
    archive = 'zipfile.zip'
    create(archive)
    print_info(archive)
    append(archive)
    print_info(archive)
    expand_hosts(archive)
    print_info(archive)

if __name__ == '__main__': main()

值得注意的是上一次调用print_info的输出:

...
hosts
    Modified:   2010-05-20 03:40:24
    Compressed: 404 bytes
    Uncompressed:   404 bytes

hosts
    Modified:   2010-05-27 11:46:28
    Compressed: 414 bytes
    Uncompressed:   414 bytes

它没有附加到现有的arcname“hosts”,它创建了一个额外的归档成员。

  

“Je n'ai fait celle-ci plus longue que   parce que je n'ai pas eu le loisir de   la faire plus courte。“
   - Blaise Pascal

答案 1 :(得分:0)

ZIP文件格式旨在附加到。它可以添加具有相同名称的其他文件,并将提取最后一个,但ZipFile不是设计为同时读写。您必须关闭该文件以写出结束记录(https://hg.python.org/cpython/file/2.7/Lib/zipfile.py#l1263),然后通过open()read()方法再次读入。 (https://hg.python.org/cpython/file/2.7/Lib/zipfile.py#l933

import zipfile
from StringIO import StringIO

def Foo():
    zfile = StringIO()

    zip = zipfile.ZipFile(zfile, 'a')
    zip.writestr(
        "foo",
        "foo content")
    zip.writestr(
        "bar",
        "bar content")
    zip.close()

    zip = zipfile.ZipFile(zfile, 'r')
    foo_content = zip.read("foo")

    zip2 = zipfile.ZipFile(zfile, 'a')
    zip2.writestr(
        "foo",
        foo_content +
        "some more foo content")
    print zip2.namelist()
    print zip2.read("bar")

Foo()

输出:

pyzip.py:23: UserWarning: Duplicate name: 'foo'
  "some more foo content")
['foo', 'bar', 'foo']
bar content