web-dev-qa-db-fra.com

Comment créer une archive ZIP d'un répertoire

Comment créer une archive Zip d'une structure de répertoires en Python? 

329
Martha Yi

Comme d'autres l'ont fait remarquer, vous devriez utiliser zipfile . La documentation vous indique quelles fonctions sont disponibles, mais n'explique pas vraiment comment vous pouvez les utiliser pour compresser un répertoire entier. Je pense qu'il est plus facile d'expliquer avec un exemple de code:

#!/usr/bin/env python
import os
import zipfile

def zipdir(path, ziph):
    # ziph is zipfile handle
    for root, dirs, files in os.walk(path):
        for file in files:
            ziph.write(os.path.join(root, file))

if __== '__main__':
    zipf = zipfile.ZipFile('Python.Zip', 'w', zipfile.Zip_DEFLATED)
    zipdir('tmp/', zipf)
    zipf.close()

Adapté de: http://www.devshed.com/c/a/Python/Python-UnZipped/

412
Mark Byers

Le moyen le plus simple consiste à utiliser shutil.make_archive . Il supporte les formats Zip et tar.

import shutil
shutil.make_archive(output_filename, 'Zip', dir_name)

Si vous avez besoin de faire quelque chose de plus compliqué que de compresser tout le répertoire (par exemple, en sautant certains fichiers), vous devrez alors creuser dans le module zipfile comme l'ont suggéré d'autres.

619
crdavis

Pour ajouter le contenu de mydirectory à un nouveau fichier Zip, y compris tous les fichiers et sous-répertoires:

import os
import zipfile

zf = zipfile.ZipFile("myzipfile.Zip", "w")
for dirname, subdirs, files in os.walk("mydirectory"):
    zf.write(dirname)
    for filename in files:
        zf.write(os.path.join(dirname, filename))
zf.close()
55
Ben James

Comment créer une archive Zip d'une structure de répertoires en Python?

Dans un script Python

Dans Python 2.7+, shutil a une fonction make_archive.

from shutil import make_archive
make_archive(
  'zipfile_name', 
  'Zip',           # the archive format - or tar, bztar, gztar 
  root_dir=None,   # root for archive - current working dir if None
  base_dir=None)   # start archiving from here - cwd if None too

Ici l'archive compressée s'appellera zipfile_name.Zip. Si base_dir est plus éloigné de root_dir, il exclura les fichiers qui ne figurent pas dans le base_dir, mais archivera tout de même les fichiers du répertoire parent jusqu'au root_dir.

J'ai eu un problème en testant ceci sur Cygwin avec 2.7 - il veut un argument root_dir, pour cwd:

make_archive('zipfile_name', 'Zip', root_dir='.')

Utiliser Python depuis le shell

Vous pouvez le faire avec Python à partir du shell en utilisant également le module zipfile:

$ python -m zipfile -c zipname sourcedir

zipname est le nom du fichier de destination que vous voulez (ajoutez .Zip si vous le voulez, cela ne se fera pas automatiquement) et sourcedir est le chemin du répertoire.

Zipper vers le haut Python (ou tout simplement ne pas vouloir de répertoire parent):

Si vous essayez de compresser un paquet python avec un __init__.py et un __main__.py et que vous ne voulez pas du répertoire parent, 

$ python -m zipfile -c zipname sourcedir/*

Et

$ python zipname

lancerait le paquet. (Notez que vous ne pouvez pas exécuter de sous-packages en tant que point d’entrée à partir d’une archive compressée.)

Zipper une application Python:

Si vous avez python3.5 + et que vous voulez spécifiquement compresser un paquet Python, utilisez zipapp :

$ python -m zipapp myapp
$ python myapp.pyz
43
Aaron Hall

Cette fonction va récursivement compresser une arborescence de répertoires, compresser les fichiers et enregistrer les noms de fichiers relatifs corrects dans l'archive. Les entrées d'archive sont les mêmes que celles générées par Zip -r output.Zip source_dir.

import os
import zipfile
def make_zipfile(output_filename, source_dir):
    relroot = os.path.abspath(os.path.join(source_dir, os.pardir))
    with zipfile.ZipFile(output_filename, "w", zipfile.Zip_DEFLATED) as Zip:
        for root, dirs, files in os.walk(source_dir):
            # add directory (needed for empty dirs)
            Zip.write(root, os.path.relpath(root, relroot))
            for file in files:
                filename = os.path.join(root, file)
                if os.path.isfile(filename): # regular files only
                    arcname = os.path.join(os.path.relpath(root, relroot), file)
                    Zip.write(filename, arcname)
29
George V. Reilly

Utilisez shutil, qui fait partie du jeu de bibliothèques standard Python. Utiliser shutil est si simple (voir code ci-dessous): 

  • 1er argument: nom du fichier Zip/tar résultant, 
  • 2ème argument: Zip/tar, 
  • 3ème argument: dir_name

Code:

import shutil
shutil.make_archive('/home/user/Desktop/Filename','Zip','/home/username/Desktop/Directory')
11
vadiraj jahagirdar

Pour ajouter une compression au fichier Zip résultant, consultez ce lien .

Vous devez changer:

Zip = zipfile.ZipFile('Python.Zip', 'w')

à 

Zip = zipfile.ZipFile('Python.Zip', 'w', zipfile.Zip_DEFLATED)
11
E Smith

J'ai apporté quelques modifications à code donné par Mark Byers . La fonction ci-dessous ajoute également des répertoires vides si vous en avez. Les exemples devraient indiquer plus clairement quel est le chemin ajouté au fichier Zip.

#!/usr/bin/env python
import os
import zipfile

def addDirToZip(zipHandle, path, basePath=""):
    """
    Adding directory given by \a path to opened Zip file \a zipHandle

    @param basePath path that will be removed from \a path when adding to archive

    Examples:
        # add whole "dir" to "test.Zip" (when you open "test.Zip" you will see only "dir")
        zipHandle = zipfile.ZipFile('test.Zip', 'w')
        addDirToZip(zipHandle, 'dir')
        zipHandle.close()

        # add contents of "dir" to "test.Zip" (when you open "test.Zip" you will see only it's contents)
        zipHandle = zipfile.ZipFile('test.Zip', 'w')
        addDirToZip(zipHandle, 'dir', 'dir')
        zipHandle.close()

        # add contents of "dir/subdir" to "test.Zip" (when you open "test.Zip" you will see only contents of "subdir")
        zipHandle = zipfile.ZipFile('test.Zip', 'w')
        addDirToZip(zipHandle, 'dir/subdir', 'dir/subdir')
        zipHandle.close()

        # add whole "dir/subdir" to "test.Zip" (when you open "test.Zip" you will see only "subdir")
        zipHandle = zipfile.ZipFile('test.Zip', 'w')
        addDirToZip(zipHandle, 'dir/subdir', 'dir')
        zipHandle.close()

        # add whole "dir/subdir" with full path to "test.Zip" (when you open "test.Zip" you will see only "dir" and inside it only "subdir")
        zipHandle = zipfile.ZipFile('test.Zip', 'w')
        addDirToZip(zipHandle, 'dir/subdir')
        zipHandle.close()

        # add whole "dir" and "otherDir" (with full path) to "test.Zip" (when you open "test.Zip" you will see only "dir" and "otherDir")
        zipHandle = zipfile.ZipFile('test.Zip', 'w')
        addDirToZip(zipHandle, 'dir')
        addDirToZip(zipHandle, 'otherDir')
        zipHandle.close()
    """
    basePath = basePath.rstrip("\\/") + ""
    basePath = basePath.rstrip("\\/")
    for root, dirs, files in os.walk(path):
        # add dir itself (needed for empty dirs
        zipHandle.write(os.path.join(root, "."))
        # add files
        for file in files:
            filePath = os.path.join(root, file)
            inZipPath = filePath.replace(basePath, "", 1).lstrip("\\/")
            #print filePath + " , " + inZipPath
            zipHandle.write(filePath, inZipPath)

Ci-dessus, une fonction simple qui devrait fonctionner pour des cas simples. Vous pouvez trouver une classe plus élégante dans Gist: https://Gist.github.com/Eccenux/17526123107ca0ac28e6

5
Nux

Vous voudrez probablement regarder le module zipfile; il y a de la documentation à http://docs.python.org/library/zipfile.html .

Vous pouvez également vouloir que os.walk() indexe la structure de répertoires.

2
me_and

J'ai un autre exemple de code qui peut aider, en utilisant python3, pathlib et zipfile . Il devrait fonctionner dans n'importe quel système d'exploitation.

from pathlib import Path
import zipfile
from datetime import datetime

DATE_FORMAT = '%y%m%d'


def date_str():
    """returns the today string year, month, day"""
    return '{}'.format(datetime.now().strftime(DATE_FORMAT))


def Zip_name(path):
    """returns the Zip filename as string"""
    cur_dir = Path(path).resolve()
    parent_dir = cur_dir.parents[0]
    Zip_filename = '{}/{}_{}.Zip'.format(parent_dir, cur_dir.name, date_str())
    p_Zip = Path(Zip_filename)
    n = 1
    while p_Zip.exists():
        Zip_filename = ('{}/{}_{}_{}.Zip'.format(parent_dir, cur_dir.name,
                                             date_str(), n))
        p_Zip = Path(Zip_filename)
        n += 1
    return Zip_filename


def all_files(path):
    """iterator returns all files and folders from path as absolute path string
    """
    for child in Path(path).iterdir():
        yield str(child)
        if child.is_dir():
            for grand_child in all_files(str(child)):
                yield str(Path(grand_child))


def Zip_dir(path):
    """generate a Zip"""
    Zip_filename = Zip_name(path)
    Zip_file = zipfile.ZipFile(Zip_filename, 'w')
    print('create:', Zip_filename)
    for file in all_files(path):
        print('adding... ', file)
        Zip_file.write(file)
    Zip_file.close()


if __== '__main__':
    Zip_dir('.')
    print('end!')
2
duncanmonty

Moderne Python (3.6+) utilisant le module pathlib pour une gestion concise des chemins, ressemblant à une POO, et pathlib.Path.rglob() pour un déplacement récurrent. Autant que je sache, cela équivaut à la réponse de George V. Reilly: zips avec compression, l'élément le plus haut est un répertoire, conserve les répertoires vides, utilise des chemins relatifs. 

from pathlib import Path
from zipfile import Zip_DEFLATED, ZipFile

from os import PathLike
from typing import Union


def Zip_dir(Zip_name: str, source_dir: Union[str, PathLike]):
    src_path = Path(source_dir).expanduser().resolve(strict=True)
    with ZipFile(Zip_name, 'w', Zip_DEFLATED) as zf:
        for file in src_path.rglob('*'):
            zf.write(file, file.relative_to(src_path.parent))

Remarque: comme l'indiquent les indications de type facultatives, Zip_name ne peut pas être un objet Path ( serait corrigé dans 3.6.2+ ).

1
monk-time

Essayez celui ci-dessous. Il a travaillé pour moi

import zipfile, os
zipf = "compress.Zip"  
def main():
    directory = r"Filepath"
    toZip(directory)
def toZip(directory):
    zippedHelp = zipfile.ZipFile(zipf, "w", compression=zipfile.Zip_DEFLATED )

    list = os.listdir(directory)
    for file_list in list:
        file_name = os.path.join(directory,file_list)

        if os.path.isfile(file_name):
            print file_name
            zippedHelp.write(file_name)
        else:
            addFolderToZip(zippedHelp,file_list,directory)
            print "---------------Directory Found-----------------------"
    zippedHelp.close()

def addFolderToZip(zippedHelp,folder,directory):
    path=os.path.join(directory,folder)
    print path
    file_list=os.listdir(path)
    for file_name in file_list:
        file_path=os.path.join(path,file_name)
        if os.path.isfile(file_path):
            zippedHelp.write(file_path)
        Elif os.path.isdir(file_name):
            print "------------------sub directory found--------------------"
            addFolderToZip(zippedHelp,file_name,path)


if __name__=="__main__":
    main()
1
Chandra

Voici une variante de la réponse donnée par Nux qui fonctionne pour moi:

def WriteDirectoryToZipFile( zipHandle, srcPath, zipLocalPath = "", zipOperation = zipfile.Zip_DEFLATED ):
    basePath = os.path.split( srcPath )[ 0 ]
    for root, dirs, files in os.walk( srcPath ):
        p = os.path.join( zipLocalPath, root [ ( len( basePath ) + 1 ) : ] )
        # add dir
        zipHandle.write( root, p, zipOperation )
        # add files
        for f in files:
            filePath = os.path.join( root, f )
            fileInZipPath = os.path.join( p, f )
            zipHandle.write( filePath, fileInZipPath, zipOperation )
1
M Katz

Si vous souhaitez une fonctionnalité telle que le dossier de compression de n'importe quel gestionnaire de fichiers graphique commun, vous pouvez utiliser le code suivant. Il utilise le module zipfile . En utilisant ce code, vous aurez le fichier Zip avec le chemin comme dossier racine. 

import os
import zipfile

def zipdir(path, ziph):
    # Iterate all the directories and files
    for root, dirs, files in os.walk(path):
        # Create a prefix variable with the folder structure inside the path folder. 
        # So if a file is at the path directory will be at the root directory of the Zip file
        # so the prefix will be empty. If the file belongs to a containing folder of path folder 
        # then the prefix will be that folder.
        if root.replace(path,'') == '':
                prefix = ''
        else:
                # Keep the folder structure after the path folder, append a '/' at the end 
                # and remome the first character, if it is a '/' in order to have a path like 
                # folder1/folder2/file.txt
                prefix = root.replace(path, '') + '/'
                if (prefix[0] == '/'):
                        prefix = prefix[1:]
        for filename in files:
                actual_file_path = root + '/' + filename
                zipped_file_path = prefix + filename
                zipf.write( actual_file_path, zipped_file_path)


zipf = zipfile.ZipFile('Python.Zip', 'w', zipfile.Zip_DEFLATED)
zipdir('/tmp/justtest/', zipf)
zipf.close()
1
VGe0rge

Fonction pour créer un fichier Zip. 

def CREATEZIPFILE(zipname, path):
    #function to create a Zip file
    #Parameters: zipname - name of the Zip file; path - name of folder/file to be put in Zip file

    zipf = zipfile.ZipFile(zipname, 'w', zipfile.Zip_DEFLATED)
    zipf.setpassword(b"password") #if you want to set password to zipfile

    #checks if the path is file or directory
    if os.path.isdir(path):
        for files in os.listdir(path):
            zipf.write(os.path.join(path, files), files)

    Elif os.path.isfile(path):
        zipf.write(os.path.join(path), path)
    zipf.close()
0
sushh

Pour donner plus de flexibilité, par exemple sélectionnez répertoire/fichier par nom, utilisez:

import os
import zipfile

def zipall(ob, path, rel=""):
    basename = os.path.basename(path)
    if os.path.isdir(path):
        if rel == "":
            rel = basename
        ob.write(path, os.path.join(rel))
        for root, dirs, files in os.walk(path):
            for d in dirs:
                zipall(ob, os.path.join(root, d), os.path.join(rel, d))
            for f in files:
                ob.write(os.path.join(root, f), os.path.join(rel, f))
            break
    Elif os.path.isfile(path):
        ob.write(path, os.path.join(rel, basename))
    else:
        pass

Pour un arbre de fichiers:

.
├── dir
│   ├── dir2
│   │   └── file2.txt
│   ├── dir3
│   │   └── file3.txt
│   └── file.txt
├── dir4
│   ├── dir5
│   └── file4.txt
├── listdir.Zip
├── main.py
├── root.txt
└── selective.Zip

Vous pouvez par exemple sélectionnez uniquement dir4 et root.txt:

cwd = os.getcwd()
files = [os.path.join(cwd, f) for f in ['dir4', 'root.txt']]

with zipfile.ZipFile("selective.Zip", "w" ) as myzip:
    for f in files:
        zipall(myzip, f)

Ou simplement listdir dans le répertoire d’appel du script et ajoutez tout ce qui suit:

with zipfile.ZipFile("listdir.Zip", "w" ) as myzip:
    for f in os.listdir():
        if f == "listdir.Zip":
            # Creating a listdir.Zip in the same directory
            # will include listdir.Zip inside itself, beware of this
            continue
        zipall(myzip, f)
0
pbn

J'ai préparé une fonction en consolidant la solution de Mark Byers avec les commentaires de Reimund et Morten Zilmer (chemin relatif et répertoires vides inclus). Comme meilleure pratique, with est utilisé dans la construction du fichier de ZipFile. 

La fonction prépare également un nom de fichier Zip par défaut avec le nom du répertoire zippé et l'extension ".Zip". Par conséquent, cela fonctionne avec un seul argument: le répertoire source à compresser.

import os
import zipfile

def Zip_dir(path_dir, path_file_Zip=''):
if not path_file_Zip:
    path_file_Zip = os.path.join(
        os.path.dirname(path_dir), os.path.basename(path_dir)+'.Zip')
with zipfile.ZipFile(path_file_Zip, 'wb', zipfile.Zip_DEFLATED) as Zip_file:
    for root, dirs, files in os.walk(path_dir):
        for file_or_dir in files + dirs:
            Zip_file.write(
                os.path.join(root, file_or_dir),
                os.path.relpath(os.path.join(root, file_or_dir),
                                os.path.join(path_dir, os.path.pardir)))
0
Gürol Canbek

Voici une approche moderne, utilisant pathlib, et un gestionnaire de contexte. Place les fichiers directement dans le zip, plutôt que dans un sous-dossier.

def Zip_dir(filename: str, dir_to_Zip: pathlib.Path):
    with zipfile.ZipFile(filename, 'w', zipfile.Zip_DEFLATED) as zipf:
        # Use glob instead of iterdir(), to cover all subdirectories.
        for directory in dir_to_Zip.glob('**'):
            for file in directory.iterdir():
                if not file.is_file():
                    continue
                # Strip the first component, so we don't create an uneeded subdirectory
                # containing everything.
                Zip_path = pathlib.Path(*file.parts[1:])
                # Use a string, since zipfile doesn't support pathlib  directly.
                zipf.write(str(file), str(Zip_path))
0
Turtles Are Cute

Après avoir lu les suggestions, je suis parvenu à une méthode très similaire qui fonctionne avec 2.7.x sans créer de noms de répertoire "amusants" (noms de type absolu), et ne créera que le dossier spécifié dans le zip.

Ou juste au cas où vous auriez besoin que votre Zip contienne un dossier contenant le contenu du répertoire sélectionné.

def zipDir( path, ziph ) :
 """
 Inserts directory (path) into zipfile instance (ziph)
 """
 for root, dirs, files in os.walk( path ) :
  for file in files :
   ziph.write( os.path.join( root, file ) , os.path.basename( os.path.normpath( path ) ) + "\\" + file )

def makeZip( pathToFolder ) :
 """
 Creates a Zip file with the specified folder
 """
 zipf = zipfile.ZipFile( pathToFolder + 'file.Zip', 'w', zipfile.Zip_DEFLATED )
 zipDir( pathToFolder, zipf )
 zipf.close()
 print( "Zip file saved to: " + pathToFolder)

makeZip( "c:\\path\\to\\folder\\to\\insert\\into\\zipfile" )
0
Xedret

Supposons que vous souhaitiez compresser tous les dossiers (sous-répertoires) du répertoire actuel.

    for root, dirs, files in os.walk("."):

        for sub_dir in dirs:

            Zip_you_want = sub_dir+".Zip"

            Zip_process = zipfile.ZipFile(Zip_you_want, "w", zipfile.Zip_DEFLATED)
            Zip_process.write(file_you_want_to_include)
            Zip_process.close()

            print("Successfully zipped directory: {sub_dir}".format(sub_dir=sub_dir))
0
ShiningGo
# import required python modules
# You have to install zipfile package using pip install

import os,zipfile

# Change the directory where you want your new Zip file to be

os.chdir('Type your destination')

# Create a new zipfile ( I called it myfile )

zf = zipfile.ZipFile('myfile.Zip','w')

# os.walk gives a directory tree. Access the files using a for loop

for dirnames,folders,files in os.walk('Type your directory'):
    zf.write('Type your Directory')
    for file in files:
        zf.write(os.path.join('Type your directory',file))
0
Praveen

Pour conserver de façon concise la hiérarchie des dossiers dans le répertoire parent à archiver:

import glob
import zipfile

with zipfile.ZipFile(fp_Zip, "w", zipfile.Zip_DEFLATED) as zipf:
    for fp in glob(os.path.join(parent, "**/*")):
        base = os.path.commonpath([parent, fp])
        zipf.write(fp, arcname=fp.replace(base, ""))

Si vous le souhaitez, vous pouvez changer ceci pour utiliser pathlibpour la suppression de fichier .

0
ryanjdillon