'How to compress folders using zipfile?

My program so far is successfully able to add and compress files which are present in the same directory as test.py but there is also a folder named location in the same directory which contains additional files say citynames.txt What happens is when I do a simple zip.write (The logic used in else block) location is added to the zip file however its empty that is it does not contain citynames.txt inside it for some reason please help me add the folder itself somehow?

import os
import zipfile
import shutil
dir_path = os.path.dirname(os.path.realpath(__file__))  #holds the directory where script is located
os.chdir(dir_path)                                      #Changes to directory where script is located
fp=os.listdir(dir_path)                                 #file pointer

directory_size=len(os.listdir(dir_path))

zip1=zipfile.ZipFile('Archive.zip','w')

for i in range(directory_size) :
   if fp[i]=='test.py':
      break
   if fp[i]=='location':
      #Some code needs to be added here to write all the contents of folder "location" into 
       "Archive.zip"

   else:
       zip1.write(fp[i],compress_type=zipfile.ZIP_DEFLATED)

print("Process completed")

Assuming location has further sub-folders how to zip the files inside them aswell?



Solution 1:[1]

Listing [Python 3.Docs]: zipfile - Work with ZIP archives.

The (main) problem in your code is that you're mistaking continue by break. That way, when test.py is encountered, all the items after it will be ignored, as break exits the loop.

Here's a variant that recurses into subdirectories using [Python 3.Docs]: glob.iglob(pathname, *, recursive=False) and adds files in the archive.

code00.py:

#!/usr/bin/env python

import sys
import os
import glob
import zipfile


def main(*argv):
    archive_name = "archive.zip"
    dir_path = os.path.dirname(os.path.realpath(__file__))
    dir_path_len = len(dir_path) + len(os.path.sep)
    with zipfile.ZipFile(archive_name, mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
        for file_name in glob.iglob(os.path.join(dir_path, "**"), recursive=True):
            if os.path.isdir(file_name):
                continue
            member_name = file_name[dir_path_len:]
            if member_name in [
                os.path.basename(__file__),
                archive_name,
            ]:
                continue
            print(member_name)
            zf.write(file_name, member_name)


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main(*sys.argv[1:])
    print("\nDone.")

Output:

e:\Work\Dev\StackOverflow\q059933596>tree /f /a
Folder PATH listing for volume SSD0-WORK
Volume serial number is AE9E-72AC
E:.
|   code00.py
|   file0.txt
|
\---locations
    |   location0.tzt
    |   location1.txt
    |
    \---subdir0
            subdir0file0.txt


e:\Work\Dev\StackOverflow\q059933596>"e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py
Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32

file0.txt
locations\location0.tzt
locations\location1.txt
locations\subdir0\subdir0file0.txt

Done.

e:\Work\Dev\StackOverflow\q059933596>dir *.zip
 Volume in drive E is SSD0-WORK
 Volume Serial Number is AE9E-72AC

 Directory of e:\Work\Dev\StackOverflow\q059933596

20/01/27  20:13               547 archive.zip
               1 File(s)            547 bytes
               0 Dir(s)  113,094,475,776 bytes free

Solution 2:[2]

You need to add each file in location on it's own to zip1.
Add this to the according if-branch:

for f in os.listdir("location"):
    zip1.write("location/"+f, compress_type=zipfile.ZIP_DEFLATED)

Solution 3:[3]

The reason you're not able to add a directory to the zip archive is because zip1.write is not recursive (It won't add files that are further down the directory tree, only what you explicitly tell it to add). You'll need to use os.walk to go through all sub-directories and add each file individually:

import os
import zipfile


def zipdir(dir_to_archive, archive_filename):
    ziph = zipfile.ZipFile(archive_filename, 'w', zipfile.ZIP_DEFLATED)
    for root, dirs, files in os.walk(dir_to_archive):
        for file in files:
            if file != archive_filename:
                ziph.write(os.path.join(root, file))
    ziph.close()


os.chdir(dir_path)
zipdir(dir_path, 'Archive.zip')

Note, the above code was adapted from this answer by Mark Byers.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 jofrev
Solution 3