我有一个包含以下目录结构的 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。
有没有快速解压文件和维护目录结构的方法?
如果您使用的是 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 文件的内容),但您明白了。
不要相信 extract() 或 extractall()。
这些方法会盲目地将文件提取到文件名中给出的路径。但是 ZIP 文件名可以是任何东西,包括像“x/../../../etc/passwd”这样的危险字符串。提取此类文件,您可能已经破坏了整个服务器。
也许这应该被认为是 Python 的 zipfile 模块中的一个可报告的安全漏洞,但过去任何数量的 zip-dearchivers 都表现出完全相同的行为。要安全地解压缩具有文件夹结构的 ZIP 文件,您需要深入检查每个文件路径。
我试过了,并且可以重现它。正如其他答案所建议的, 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
;如果是目录,则创建目录。否则,提取文件。
我知道现在说这个可能有点晚,但杰夫是对的。它很简单:
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)
如果您使用的是 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)
听起来您正在尝试运行 unzip 来提取 zip。
最好使用 pythonzipfile
模块,因此在 python 中进行提取。
import zipfile
def extract(zipfilepath, extractiondir):
zip = zipfile.ZipFile(zipfilepath)
zip.extractall(path=extractiondir)
您所要做的就是过滤掉以namelist()
结尾的条目/
,问题就解决了:
z.extractall(dest, filter(lambda f: not f.endswith('/'), z.namelist()))
开心!
如果像我一样,您必须使用较旧的 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()
请注意,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 配置中表现得很奇怪。