Python zipfile不会解压缩Windows zip存档的文件夹

时间:2018-08-30 06:51:42

标签: python linux python-3.x archive zipfile

我有一个使用此工具WindowsSystem.IO.Compression.ZipFile机器上创建的zip文件(此zip归档文件包含许多文件和文件夹)。我有一个在Linux计算机(确切地说是树莓派)上运行的python代码,必须解压缩存档文件并创建所有必要的文件夹和文件。我正在使用Python 3.5.0zipfile库,这是示例代码:

import zipfile

zip = zipfile.ZipFile("MyArchive.zip","r")
zip.extractall()
zip.close()

现在,当我运行此代码而不是获得漂亮的未压缩目录树时,我会在根目录中获得所有带有奇怪名称(例如Folder1\Folder2\MyFile.txt)的文件。

我的假设是,由于zip归档文件是在Windows上创建的,而Windows上的目录分隔符是\,而在Linux上是/,因此Python zipfile库将\视为文件名的一部分,而不是目录分隔符。另请注意,当我手动提取该归档文件(而不是通过python代码)时,所有文件夹均按预期方式创建,因此看来这绝对是zipfile库的问题。另一个注意事项是,对于使用其他工具(而非System.IO.Compression.ZipFile)创建的zip归档文件,使用相同的python代码可以正常工作。

对正在发生的事情以及如何解决它有任何见解吗?

2 个答案:

答案 0 :(得分:3)

正在发生的事情是,尽管Windows将\path.sep)和/path.altsep)都识别为路径分隔符,但Linux仅识别/path.sep

@blhsing's answer所示,ZipFile的现有实现始终确保将path.sep/视为有效的分隔符。这意味着在Linux上,\被视为文件名的文字部分。要更改此设置,您可以将os.altsep设置为\,因为会检查它是否不为空。

如果您像其他答案所建议的那样自行修改ZipFile,只需添加一行以将\盲目地更改为path.sep,因为/是总是已经改变了。这样,/\以及可能的path.altsep将全部转换为path.sep。这就是命令行工具正在执行的操作。

答案 1 :(得分:1)

这确实是zipfile module的错误,其中ZipFile._extract_member()中有以下行,当使用操作系统特定的路径分隔符盲目替换文件名中的'/'时,还应该寻找'\\'

arcname = member.filename.replace('/', os.path.sep)

您可以通过以下方法解决此问题:将ZipFile._extract_member()替换为直接从源代码复制但已修正上述行的版本:

from zipfile import ZipFile, ZipInfo
import shutil
import os
def _extract_member(self, member, targetpath, pwd):
    """Extract the ZipInfo object 'member' to a physical
       file on the path targetpath.
    """
    if not isinstance(member, ZipInfo):
        member = self.getinfo(member)

    if os.path.sep == '/':
        arcname = member.filename.replace('\\', os.path.sep)
    else:
        arcname = member.filename.replace('/', os.path.sep)

    if os.path.altsep:
        arcname = arcname.replace(os.path.altsep, os.path.sep)
    # interpret absolute pathname as relative, remove drive letter or
    # UNC path, redundant separators, "." and ".." components.
    arcname = os.path.splitdrive(arcname)[1]
    invalid_path_parts = ('', os.path.curdir, os.path.pardir)
    arcname = os.path.sep.join(x for x in arcname.split(os.path.sep)
                               if x not in invalid_path_parts)
    if os.path.sep == '\\':
        # filter illegal characters on Windows
        arcname = self._sanitize_windows_name(arcname, os.path.sep)

    targetpath = os.path.join(targetpath, arcname)
    targetpath = os.path.normpath(targetpath)

    # Create all upper directories if necessary.
    upperdirs = os.path.dirname(targetpath)
    if upperdirs and not os.path.exists(upperdirs):
        os.makedirs(upperdirs)

    if member.is_dir():
        if not os.path.isdir(targetpath):
            os.mkdir(targetpath)
        return targetpath

    with self.open(member, pwd=pwd) as source, \
            open(targetpath, "wb") as target:
        shutil.copyfileobj(source, target)

    return targetpath
ZipFile._extract_member = _extract_member