【问题标题】:ZipFile.testzip() returning different results on Python 2 and Python 3ZipFile.testzip() 在 Python 2 和 Python 3 上返回不同的结果
【发布时间】:2017-05-20 11:10:26
【问题描述】:

在 Python 中使用zipfile 模块解压缩大型数据文件在 Python 2 上可以正常工作,但在 Python 3.6.0 上会产生以下错误:

BadZipFile: Bad CRC-32 for file 'myfile.csv'

我将此追溯到检查 CRC 值的错误处理代码。

在 Python 2 上使用 ZipFile.testzip() 不会返回任何内容(所有文件都很好)。在 Python 3 上运行它会返回 'myfile.csv',表明该文件存在问题。

在 Python 2 和 Python 3 上重现的代码(涉及 300 MB 下载,抱歉):

import zipfile
import urllib
import sys

url = "https://de.iplantcollaborative.org/anon-files//iplant/home/shared/commons_repo/curated/Vertnet_Amphibia_Sep2016/VertNet_Amphibia_Sept2016.zip"

if sys.version_info >= (3, 0, 0):
    urllib.request.urlretrieve(url, "vertnet_latest_amphibians.zip")
else:
    urllib.urlretrieve(url, "vertnet_latest_amphibians.zip")

archive = zipfile.ZipFile("vertnet_latest_amphibians.zip")
archive.testzip()

有谁知道为什么存在这种差异,是否有办法让 Python 3 正确提取文件:

archive.extract("vertnet_latest_amphibians.csv")

【问题讨论】:

  • 看起来我在示例中加入了一些本地状态。对不起。现在应该可以顺利运行了。
  • 嗯,如果可以的话,请添加你正在使用的 Python 的确切版本来运行它。
  • 它是3.6.0。我刚刚将其添加到问题中。
  • 我可以用2.7.12(工作)和3.4.3(失败)重新创建它。我现在必须离开这个,但我确实验证了 ZipInfo 中的 CRC 在两个 Python 版本中是相同的,所以我认为这是 CRC32 计算的差异。附带说明一下,我的 7-zip 版本(自 2010 年以来我没有更新)也认为该文件已损坏。
  • 下载速度极慢,在完成之前中止。你能把文件镜像到别的地方吗?

标签: python python-3.x python-2.x unzip zipfile


【解决方案1】:

在我的情况下,与提取时文件的实际大小相比,问题是错误的ZipInfo.file_size(Python 2.7)(正如@nick-matteo 发现的那样)。我发现文件大小不匹配的原因是将 unicode 字符串传递给zipfile.writestr() 函数。

在我的情况下,解决方案是将 unicode 编码为 utf8,然后再传递给 writestr() 函数:

zf = zipfile.ZipFile(...)
if isinstance(file_contents, unicode):
    file_contents = file_contents.encode("utf8")
zf.writestr("filename.txt", file_contents)
...

【讨论】:

    【解决方案2】:

    作为@Kundor,将 file_size 设置为最大值 (2**32 - 1) 将起作用,但对于大于 4 GiB(4 GiB 减去 1 字节)的任何文件都将失败,因此将其设置为 ZIP64 的最大大小(16 EiB 减 1 个字节)

    在(927MB 压缩和 11GB 的 file_to_extract)上测试

    网址: https://de.iplantcollaborative.org/anon-files//iplant/home/shared/commons_repo/curated/Vertnet_Aves_Sep2016/VertNet_Aves_Sept2016.zip

    文件:vertnet_latest_birds.csv

    import zipfile
    import urllib
    import sys
    
    url = "https://de.iplantcollaborative.org/anon-files//iplant/home/shared/commons_repo/curated/Vertnet_Amphibia_Sep2016/VertNet_Amphibia_Sept2016.zip"
    zip_path = "vertnet_latest_amphibians.zip"
    file_to_extract = "vertnet_latest_amphibians.csv"
    
    if sys.version_info >= (3, 0, 0):
        urllib.request.urlretrieve(url, zip_path)
    else:
        urllib.urlretrieve(url, zip_path)
    
    archive = zipfile.ZipFile(zip_path)
    if archive.testzip():
        # reset uncompressed size header values to maximum
        archive.getinfo(file_to_extract).file_size += (2 ** 64) - 1
        
    open_archive_file = archive.open(file_to_extract, 'r')
    # or archive.extract(file_to_extract)
    

    【讨论】:

      【解决方案3】:

      CRC 值正常。 zip 中记录的 'vertnet_latest_amphibians.csv' 的 CRC 为 0x87203305。解压后,这确实是文件的CRC。

      但是,给定的未压缩大小不正确。 zip 文件记录的压缩大小为 309,723,024 字节,未压缩大小为 292,198,614 字节(更小!)。实际上,未压缩的文件为 4,587,165,910 字节 (4.3 GiB)。这大于 32 位计数器中断的 4 GiB 阈值。

      您可以这样修复它(至少在 Python 3.5.2 中有效):

      archive = zipfile.ZipFile("vertnet_latest_amphibians.zip")
      archive.getinfo("vertnet_latest_amphibians.csv").file_size += 2**32
      archive.testzip() # now passes
      archive.extract("vertnet_latest_amphibians.csv") # now works
      

      【讨论】:

        【解决方案4】:

        我无法从存档中提取 Python 3。调查的一些结果(在 Mac OS X 上)可能会有所帮助。

        检查档案的健康状况

        将文件设为只读以防止意外更改:

        $ chmod -w vertnet_latest_amphibians.zip 
        $ ls -lh vertnet_latest_amphibians.zip 
        -r--r--r-- 1 lawh 2045336417 296M Jan  6 10:10 vertnet_latest_amphibians.zip
        

        使用zipunzip 检查存档:

        $ zip -T vertnet_latest_amphibians.zip
        test of vertnet_latest_amphibians.zip OK
        
        $ unzip -t vertnet_latest_amphibians.zip
        Archive:  vertnet_latest_amphibians.zip
            testing: VertNet_Amphibia_eml.xml   OK
            testing: __MACOSX/                OK
            testing: __MACOSX/._VertNet_Amphibia_eml.xml   OK
            testing: vertnet_latest_amphibians.csv   OK
            testing: __MACOSX/._vertnet_latest_amphibians.csv   OK
        No errors detected in compressed data of vertnet_latest_amphibians.zip
        

        @sam-mussmann 也发现,7z 报告了 CRC 错误:

        $ 7z t vertnet_latest_amphibians.zip 
        
        7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
        p7zip Version 16.02 (locale=utf8,Utf16=on,HugeFiles=on,64 bits,4 CPUs x64)
        
        Scanning the drive for archives:
        1 file, 309726398 bytes (296 MiB)
        
        Testing archive: vertnet_latest_amphibians.zip
        --
        Path = vertnet_latest_amphibians.zip
        Type = zip
        Physical Size = 309726398
        
        ERROR: CRC Failed : vertnet_latest_amphibians.csv
        
        Sub items Errors: 1
        
        Archives with Errors: 1
        
        Sub items Errors: 1
        

        我的zipunzip 都比较老了; 7z 很新:

        $ zip -v | head -2
        Copyright (c) 1990-2008 Info-ZIP - Type 'zip "-L"' for software license.
        This is Zip 3.0 (July 5th 2008), by Info-ZIP.
        
        $ unzip -v | head -1
        UnZip 6.00 of 20 April 2009, by Debian. Original by Info-ZIP.
        
        $ 7z --help |head -3
        
        7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
        p7zip Version 16.02 (locale=utf8,Utf16=on,HugeFiles=on,64 bits,4 CPUs x64)
        

        提取

        使用unzip

        $ time unzip vertnet_latest_amphibians.zip vertnet_latest_amphibians.csv
        Archive:  vertnet_latest_amphibians.zip
          inflating: vertnet_latest_amphibians.csv  
        
        real    0m17.201s
        user    0m14.281s
        sys 0m2.460s
        

        使用 Python 2.7.13 进行提取,为简洁起见,使用 zipfile 的命令行界面:

        $ time ~/local/python-2.7.13/bin/python2 -m zipfile -e vertnet_latest_amphibians.zip .
        
        real    0m19.491s
        user    0m12.996s
        sys 0m5.897s
        

        如您所见,Python 3.6.0(也是 3.4.5 和 3.5.2)报告了错误的 CRC

        假设 1:存档包含错误的 CRC,即 zipunzip 和 Python 2.7.13 未能检测到; 7z 和 Python 3.4-3.6 都在做 正确的事情。

        假设2:存档没问题; 7z 和 Python 3.4-3.6 都包含一个错误。

        鉴于这些工具的相对年龄,我猜 H1 是正确的。

        解决方法

        如果您不使用 Windows 并且信任存档的内容,则使用常规 shell 命令可能更直接。比如:

        wget <the-long-url> -O /tmp/vertnet_latest_amphibians.zip
        unzip /tmp/vertnet_latest_amphibians.zip vertnet_latest_amphibians.csv
        rm -rf /tmp/vertnet_latest_amphibians.zip
        

        或者您可以在 Python 中执行 unzip

        import os
        os.system('unzip vertnet_latest_amphibians.zip vertnet_latest_amphibians.csv')
        

        偶然

        捕捉ImportError 比检查版本稍微简洁一些 Python 解释器:

        try:
            from urllib.request import urlretrieve
        except ImportError:
            from urllib import urlretrieve
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-03-12
          • 2018-08-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多