Search code examples
pythonzippython-zipfile

Create .zip in Python?


I'm trying to create a function in my script that zips the contents of a given source directory (src) to a zip file (dst). For example, zip('/path/to/dir', '/path/to/file.zip'), where /path/to/dir is a directory, and /path/to/file.zip doesn't exist yet. I do not want to zip the directory itself, this makes all the difference in my case. I want to zip the files (and subdirs) in the directory. This is what I'm trying:

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()

This creates a zip that is essentially /. For example, if I zipped /path/to/dir, extracting the zip creates a directory with "path" in it, with "to" in that directory, etc.

Does anyone have a function that doesn't cause this problem?

I can't stress this enough, it needs to zip the files in the directory, not the directoy itself.


Solution

  • The zipfile.write() method takes an optional arcname argument that specifies what the name of the file should be inside the zipfile.

    You can use this to strip off the path to src at the beginning. Here I use os.path.abspath() to make sure that both src and the filename returned by os.walk() have a common prefix.

    #!/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")
    

    With a directory structure like this:

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

    The script prints:

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

    And the contents of the resulting zip file are:

    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