使用Python的ZipFile库解压时出现奇怪结果

0 投票
2 回答
561 浏览
提问于 2025-04-18 14:05

我有一个压缩文件,使用Python的ZipFile库来解压时,遇到了一个奇怪的情况:压缩包根目录下的文件被解压到了一个以压缩包名字命名的子目录里。

这是我使用ZipFile库的方式:

#!/usr/bin/X11/python

import sys
import urllib
import zipfile
import os.path
import os
import tempfile

def unzip(source_filename, dest_dir):
    with zipfile.ZipFile(source_filename) as zf:
        for member in zf.infolist():
            # Path traversal defense copied from
            # http://hg.python.org/cpython/file/tip/Lib/http/server.py#l789
            words = member.filename.split('/')
            path = dest_dir
            for word in words[:-1]:
                drive, word = os.path.splitdrive(word)
                head, word = os.path.split(word)
                if word in (os.curdir, os.pardir, ''): continue
                path = os.path.join(path, word)
            zf.extract(member, path)

try:
    # Get the latest release
    print 'Downloading stack archive...'
    (vagrantstack, infoheaders) = urllib.urlretrieve ('https://github.com/jquery/globalize/archive/master.zip')

    # Unzip in the project folder
    print 'Unzipping...'
    unzip(vagrantstack, '.')

finally:
    urllib.urlcleanup()

我本来是想还原jquery/globalize这个项目的结构,但结果是所有根目录的文件都被放到了一个子目录里……

有没有人能指出这里的问题呢?

声明一下:解压的功能不是我写的,但我觉得它是正确的。

补充:这是我得到的输出:

➜  test-py  ./test.py
Downloading stack archive...
Unzipping...
➜  test-py  ls -l
total 8
drwxr-xr-x 6 adrien adrien 4096 juil. 21 12:23 globalize-master
-rwxr-xr-x 1 adrien adrien 1032 juil. 21 12:23 test.py
➜  test-py  ls -l globalize-master
total 16
drwxr-xr-x 5 adrien adrien 4096 juil. 21 12:23 doc
drwxr-xr-x 2 adrien adrien 4096 juil. 21 12:23 globalize-master
drwxr-xr-x 8 adrien adrien 4096 juil. 21 12:23 src
drwxr-xr-x 6 adrien adrien 4096 juil. 21 12:23 test
➜  test-py  ls -l globalize-master/globalize-master
total 40
-rw-r--r-- 1 adrien adrien   354 juil. 21 12:23 bower.json
-rw-r--r-- 1 adrien adrien  1052 juil. 21 12:23 CONTRIBUTING.md
-rw-r--r-- 1 adrien adrien  6809 juil. 21 12:23 Gruntfile.js
-rw-r--r-- 1 adrien adrien  1826 juil. 21 12:23 LICENSE.txt
-rw-r--r-- 1 adrien adrien  2397 juil. 21 12:23 package.json
-rw-r--r-- 1 adrien adrien 14151 juil. 21 12:23 README.md
➜  test-py

globalize-master/globalize-master这个文件夹不应该存在,它的内容应该在根目录下。

2 个回答

0

肯定是在 unzip() 这个地方出了问题。它在所有目录下都创建了一个 globalize-master 的子目录,而不仅仅是在根目录下。

如果你信任这个压缩文件的来源,你可以直接使用 zf.extractall(dest_dir)extractall() 在 Python 2.7.4 及之后的版本应该是安全的。

2

你尝试下载的压缩文件没有顶层文件。这个压缩包里面只有一个叫做 globalize-master 的文件夹,所有的文件都在这个文件夹里,所以你看到的情况是正常的。

如果你用 unzip 来解压内容,你会看到同样的情况:

$ls
globalize-master.zip
$unzip globalize-master.zip 
Archive:  globalize-master.zip
300a9dc6cb4a08eb847c8565ee01eae4cd9aa35c
   creating: globalize-master/
 extracting: globalize-master/.bowerrc  
  [...]
  inflating: globalize-master/test/util.js  
$ls -l
totale 116
drwxrwxr-x 5 username username   4096 lug 13 07:35 globalize-master
-rw-r--r-- 1 username username 113313 lug 21 12:44 globalize-master.zip

从源代码来看,unzip 函数对文件名所做的处理其实是没用的,因为这些事情已经由 ZipFile.extract 处理了。正确的 unzip 版本是:

def unzip(source_filename, dest_dir):
    with zipfile.ZipFile(source_filename) as zf:
        for member in zf.infolist():
            zf.extract(member, dest_dir)

这样就能得到预期的结果。

需要注意的是,这和使用 extractall 方法基本上是一样的:

def unzip(source_filename, dest_dir):
    with zipfile.ZipFile(source_filename) as zf:
        zf.extractall(dest_dir)

撰写回答