在Python中创建.zip文件?

37
我正在尝试在我的脚本中创建一个函数,将给定源目录(src)的内容压缩到一个zip文件(dst)中。例如,zip('/path/to/dir', '/path/to/file.zip'),其中/path/to/dir是一个目录,/path/to/file.zip尚不存在。我不想压缩目录本身,在我的情况下这很重要。我想压缩目录中的文件(和子目录)。这是我的尝试:
def zip(src, dst):
    zf = zipfile.ZipFile("%s.zip" % (dst), "w")
    for dirname, subdirs, files in os.walk(src):
        zf.write(dirname)
        for filename in files:
            zf.write(os.path.join(dirname, filename))
    zf.close()

这将创建一个基本上是/的压缩包。例如,如果我压缩了/path/to/dir,解压缩该压缩包将创建一个带有“path”的目录,该目录中包含“to”,以此类推。

有没有不会导致这个问题的函数?

我强调一遍,它需要压缩目录中的文件,而不是目录本身。


可能是重复的问题,参考如何创建目录的zip归档文件 - BuZZ-dEE
3个回答

57

zipfile.write() 方法有一个可选参数 arcname,用于指定文件在压缩包中的名称。

你可以使用它来去除开头的路径到 src。这里我使用os.path.abspath()确保srcos.walk()返回的文件名有共同的前缀。

#!/usr/bin/env python2.7

import os
import zipfile

def zip(src, dst):
    zf = zipfile.ZipFile("%s.zip" % (dst), "w", zipfile.ZIP_DEFLATED)
    abs_src = os.path.abspath(src)
    for dirname, subdirs, files in os.walk(src):
        for filename in files:
            absname = os.path.abspath(os.path.join(dirname, filename))
            arcname = absname[len(abs_src) + 1:]
            print 'zipping %s as %s' % (os.path.join(dirname, filename),
                                        arcname)
            zf.write(absname, arcname)
    zf.close()

zip("src", "dst")

像这样的目录结构:

src
└── a
    ├── b
    │   └── bar
    └── foo

这个脚本会打印出:

zipping src/a/foo as a/foo
zipping src/a/b/bar as a/b/bar

生成的 zip 文件的内容为:

Archive:  dst.zip
  Length     Date   Time    Name
 --------    ----   ----    ----
        0  01-28-13 11:36   a/foo
        0  01-28-13 11:36   a/b/bar
 --------                   -------
        0                   2 files

是的 - os.walk() 使用 os,而 os.path.abspath()os.path.join() 使用 os.path - andrewdotn
@agoebel 另外,顺便问一下,import os.pathfrom os import path 有什么区别? - tkbx
2
@tkbx: from os import pathpath 放在顶层,因此您可以使用 path.join 而不是 os.path.join。这通常不是您想要做的(特别是因为每个人都在他们的代码中有一个名为 path 的变量)。 - abarnert
1
@tkbx:不,除非argvsys的子模块,否则你不能import sys.argv。但是argv不是一个模块,它只是一个列表。但是当你通常做的事情——import sys之后,你就可以使用script, vars = sys.argv。虽然实际上,你也不会经常写这个,因为如果没有命令行参数或者有两个命令行参数,你会得到一个ValueError - abarnert
2
这个函数运行良好,但它不会将空文件夹添加到zip文件中,在大多数情况下这是期望的行为。换句话说,任何没有文件的子文件夹都将被忽略。 - bobyuan
显示剩余5条评论

1
据我所知,你已经很接近了。你可以使用dirnamebasename来确保获取正确的路径名:
>>> os.path.dirname("/path/to/dst")
'/path/to'
>>> os.path.basename("/path/to/dst")
'dst'

然后使用chdir,确保你在父目录中,这样路径就是相对的。

def zip(src, dst):
    parent = os.path.dirname(dst)
    folder = os.path.basename(dst)

    os.chdir(parent):
    for dirname, subdirs, filenames in os.walk(folder):
        ...

这会创建:
dst/a.txt
dst/b
dst/b/c.txt
...etc...

如果不想包括名称“dst”,只需执行 os.chdir(dst),然后执行 os.walk('。')

希望这能有所帮助。


请注意,zip也是一个具有非常不同目的的内置函数:https://docs.python.org/2/library/functions.html#zip - Matteo T.

1
使用arcname参数控制zip文件中的名称/路径。
例如,对于仅包含文件而不包含目录的zip文件:
zf.write(os.path.join(dirname, filename), arcname=filename)

或者在zip文件内创建一个新的目录:
zf.write(os.path.join(dirname, filename), arcname=os.path.join("my_zip_dir", filename))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接