29

我有一个包含以下目录结构的 zip 文件:

dir1\dir2\dir3a
dir1\dir2\dir3b

我正在尝试解压缩并维护目录结构,但是出现错误:

IOError: [Errno 2] No such file or directory: 'C:\\\projects\\\testFolder\\\subdir\\\unzip.exe'

其中 testFolder 是上面的 dir1,subdir 是 dir2。

有没有快速解压文件和维护目录结构的方法?

4

9 回答 9

23

如果您使用的是 Python 2.6,则 extract 和 extractall 方法非常有用。我现在必须使用 Python 2.5,所以如果它们不存在,我只需要创建目录。namelist()您可以使用该方法获取目录列表。目录将始终以正斜杠结尾(即使在 Windows 上),例如,

import os, zipfile

z = zipfile.ZipFile('myfile.zip')
for f in z.namelist():
    if f.endswith('/'):
        os.makedirs(f)

您可能不想完全那样做(即,您可能希望在遍历名称列表时提取 zip 文件的内容),但您明白了。

于 2009-03-12T19:11:36.257 回答
16

不要相信 extract() 或 extractall()。

这些方法会盲目地将文件提取到文件名中给出的路径。但是 ZIP 文件名可以是任何东西,包括像“x/../../../etc/passwd”这样的危险字符串。提取此类文件,您可能已经破坏了整个服务器。

也许这应该被认为是 Python 的 zipfile 模块中的一个可报告的安全漏洞,但过去任何数量的 zip-dearchivers 都表现出完全相同的行为。要安全地解压缩具有文件夹结构的 ZIP 文件,您需要深入检查每个文件路径。

于 2009-03-13T00:56:12.760 回答
8

我试过了,并且可以重现它。正如其他答案所建议的, extractall 方法不能解决问题。对我来说,这似乎是 zipfile 模块中的一个错误(可能仅限 Windows?),除非我误解了 zipfile 的结构。

testa\
testa\testb\
testa\testb\test.log
> test.zip

>>> from zipfile import ZipFile
>>> zipTest = ZipFile("C:\\...\\test.zip")
>>> zipTest.extractall("C:\\...\\")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "...\zipfile.py", line 940, in extractall
  File "...\zipfile.py", line 928, in extract
  File "...\zipfile.py", line 965, in _extract_member
IOError: [Errno 2] No such file or directory: 'C:\\...\\testa\\testb\\test.log'

如果我做 a printdir(),我会得到这个(第一列):

>>> zipTest.printdir()
File Name
testa/testb/
testa/testb/test.log

如果我尝试仅提取第一个条目,如下所示:

>>> zipTest.extract("testa/testb/")
'C:\\...\\testa\\testb'

在磁盘上,这会导致创建一个文件夹testa,其中包含一个文件 testb。这显然是随后尝试提取test.log失败的原因;testa\testb是文件,不是文件夹。

编辑#1:如果您只提取文件,那么它可以工作:

>>> zipTest.extract("testa/testb/test.log")
'C:\\...\\testa\\testb\\test.log'

编辑#2:杰夫的代码是要走的路;遍历namelist;如果是目录,则创建目录。否则,提取文件。

于 2009-03-12T19:11:42.667 回答
6

我知道现在说这个可能有点晚,但杰夫是对的。它很简单:

import os
from zipfile import ZipFile as zip

def extractAll(zipName):
    z = zip(zipName)
    for f in z.namelist():
        if f.endswith('/'):
            os.makedirs(f)
        else:
            z.extract(f)

if __name__ == '__main__':
    zipList = ['one.zip', 'two.zip', 'three.zip']
    for zip in zipList:
        extractAll(zipName)
于 2011-06-25T14:32:43.357 回答
3

如果您使用的是 Python 2.6,有一个非常简单的方法:extractall方法。

但是,由于该zipfile模块完全在 Python 中实现,没有任何 C 扩展,您可能可以将它从 2.6 安装中复制出来并与旧版本的 Python 一起使用;您可能会发现这比自己重新实现功能更容易。但是,函数本身很短:

def extractall(self, path=None, members=None, pwd=None):
    """Extract all members from the archive to the current working
       directory. `path' specifies a different directory to extract to.
       `members' is optional and must be a subset of the list returned
       by namelist().
    """
    if members is None:
        members = self.namelist()

    for zipinfo in members:
        self.extract(zipinfo, path, pwd)
于 2009-03-12T19:03:04.717 回答
2

听起来您正在尝试运行 unzip 来提取 zip。

最好使用 pythonzipfile模块,因此在 python 中进行提取。

import zipfile

def extract(zipfilepath, extractiondir):
    zip = zipfile.ZipFile(zipfilepath)
    zip.extractall(path=extractiondir)
于 2009-03-12T19:03:11.160 回答
2

过滤名单以排除文件夹

您所要做的就是过滤掉以namelist()结尾的条目/,问题就解决了:

  z.extractall(dest, filter(lambda f: not f.endswith('/'), z.namelist()))

开心!

于 2012-08-26T12:20:07.567 回答
2

如果像我一样,您必须使用较旧的 Python 版本(在我的情况下为 2.4)提取完整的 zip 存档,这就是我想出的(基于 Jeff 的回答):

import zipfile
import os

def unzip(source_file_path, destination_dir):
    destination_dir += '/'
    z = zipfile.ZipFile(source_file_path, 'r')
    for file in z.namelist():
        outfile_path = destination_dir + file
        if file.endswith('/'):
            os.makedirs(outfile_path)
        else:
            outfile = open(outfile_path, 'wb')
            outfile.write(z.read(file))
            outfile.close()
    z.close()
于 2015-03-18T22:13:22.717 回答
1

请注意,zip 文件可以包含目录条目和文件条目。使用zip命令创建档案时,传递-D选项以禁用将目录条目显式添加到档案中。当 Python 2.6 的ZipFile.extractall方法在目录条目中运行时,它似乎会在其位置创建一个文件。由于存档条目不一定按顺序排列,这会导致ZipFile.extractall经常失败,因为它会尝试在文件的子目录中创建文件。如果您有要与 Python 模块一起使用的存档,只需将其解压缩并使用该-D选项重新压缩即可。这是我已经使用了一段时间的一个小片段来做到这一点:

P=`pwd` && 
Z=`mktemp -d -t zip` && 
pushd $Z && 
unzip $P/<busted>.zip && 
zip -r -D $P/<new>.zip . && 
popd && 
rm -rf $Z

用相对于当前目录的真实文件名替换<busted>.zip和。<new>.zip然后只需复制整个内容并将其粘贴到命令 shell 中,它将创建一个新的归档文件,该归档文件已准备好与 Python 2.6 一起使用。有一个命令可以zip在不解压缩的情况下删除这些目录条目,但 IIRC 在不同的 shell 环境或 zip 配置中表现得很奇怪。

于 2010-02-27T02:27:24.963 回答