【问题标题】:Python 2.6 does not like appending to existing archives in zip filesPython 2.6 不喜欢附加到 zip 文件中的现有档案
【发布时间】:2010-05-27 11:50:48
【问题描述】:

在我正在开发的程序的一些 Python 单元测试中,我们使用内存中的 zipfile 进行端到端测试。在 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:目录“bar”和标题“foo”中的文件名不同。”

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

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

有什么想法吗?

提前谢谢你!

【问题讨论】:

    标签: python zipfile


    【解决方案1】:

    PKZIP 文件是高度结构化的,仅附加到末尾就会搞砸。我不能说早期版本的工作,但解决这个问题的方法是打开一个 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.
    

    经过仔细检查,我注意到您正在从以“a”挂起模式打开的类似文件的 StringIO 中读取,这应该会导致读取错误,因为“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 放任自流。”
    - 布莱斯帕斯卡

    【讨论】:

      【解决方案2】:

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-16
        • 2014-04-15
        • 1970-01-01
        • 2022-12-10
        相关资源
        最近更新 更多