web-dev-qa-db-fra.com

Ajouter des dossiers à un fichier Zip en utilisant python

Je veux créer un fichier Zip. Ajoutez un dossier au fichier Zip, puis ajoutez un tas de fichiers à ce dossier.

Donc, je veux me retrouver avec un fichier Zip avec un seul dossier avec des fichiers.

Je ne sais pas si c'est une mauvaise pratique d'avoir des dossiers dans des fichiers Zip ou quelque chose, mais Google ne me donne rien sur le sujet.

J'ai commencé avec ça:

def addFolderToZip(myZipFile,folder):
    folder = folder.encode('ascii') #convert path to ascii for ZipFile Method
    for file in glob.glob(folder+"/*"):
            if os.path.isfile(file):
                print file
                myZipFile.write(file, os.path.basename(file), zipfile.Zip_DEFLATED)
            Elif os.path.isdir(file):
                addFolderToZip(myZipFile,file)

def createZipFile(filename,files,folders):
    curTime=strftime("__%Y_%m_%d", time.localtime())
    filename=filename+curTime;
    print filename
    zipFilename=utils.getFileName("files", filename+".Zip")
    myZipFile = zipfile.ZipFile( zipFilename, "w" ) # Open the Zip file for writing 
    for file in files:
        file = file.encode('ascii') #convert path to ascii for ZipFile Method
        if os.path.isfile(file):
            (filepath, filename) = os.path.split(file)
            myZipFile.write( file, filename, zipfile.Zip_DEFLATED )

    for folder in  folders:   
        addFolderToZip(myZipFile,folder)  
    myZipFile.close()
    return (1,zipFilename)


(success,filename)=createZipFile(planName,files,folders);

Extrait de: http://mail.python.org/pipermail/python-list/2006-August/396166.html

Ce qui supprime tous les dossiers et place tous les fichiers du dossier cible (et de ses sous-dossiers) dans un fichier Zip unique. Je ne pouvais pas l'obtenir pour ajouter un dossier entier.

Si je nourris le chemin vers un dossier dans myZipFile.write, je reçois

IOError: [Errno 13] Autorisation refusée: '..\emballé\bin'

Toute aide est la bienvenue.

Question connexe: Comment compresser le contenu d'un dossier à l'aide de python (version 2.5)?

40
Mizipzor

Ok, après avoir compris ce que vous voulez, c’est aussi simple que d’utiliser le deuxième argument de zipfile.write, où vous pouvez utiliser ce que vous voulez:

import zipfile
myZipFile = zipfile.ZipFile("Zip.zip", "w" )
myZipFile.write("test.py", "dir\\test.py", zipfile.Zip_DEFLATED )

crée un fichier zip où test.py serait extrait dans un répertoire appelé dir 

EDIT: Une fois, j’ai dû créer un répertoire vide dans un fichier Zip: c’est possible… .. après que le code ci-dessus ait supprimé le fichier test.py du fichier zip, le fichier est parti, mais le répertoire vide reste .

44
RSabet

Vous pouvez également utiliser le shutil

import shutil

Zip_name = 'path\to\Zip_file'
directory_name = 'path\to\directory'

# Create 'path\to\Zip_file.Zip'
shutil.make_archive(Zip_name, 'Zip', directory_name)

Cela mettra le dossier entier dans le zip.

50
Gideon

Un fichier Zip n'a pas de structure de répertoires, il a juste un tas de noms de chemins et leur contenu. Ces noms de chemin doivent être relatifs à un dossier racine imaginaire (le fichier Zip lui-même). Les préfixes "../" n'ont pas de signification définie dans un fichier Zip.

Considérez que vous avez un fichier, a et que vous voulez le stocker dans un "dossier" dans un fichier Zip. Tout ce que vous avez à faire est de préfixer le nom du fichier avec un nom de dossier lorsque vous stockez le fichier dans le fichier zip:

zipi= zipfile.ZipInfo()
zipi.filename= "folder/a" # this is what you want
zipi.date_time= time.localtime(os.path.getmtime("a"))[:6]
zipi.compress_type= zipfile.Zip_DEFLATED
filedata= open("a", "rb").read()

zipfile1.writestr(zipi, filedata) # zipfile1 is a zipfile.ZipFile instance

Je ne connais aucune implémentation Zip permettant l'inclusion d'un dossier empty dans un fichier Zip. Je peux penser à une solution de contournement (stocker un nom factice nom de fichier dans le "dossier" Zip qui devrait être ignoré lors de l'extraction), mais non portable à travers les implémentations.

12
tzot
import zipfile
import os


class ZipUtilities:

    def toZip(self, file, filename):
        Zip_file = zipfile.ZipFile(filename, 'w')
        if os.path.isfile(file):
                    Zip_file.write(file)
            else:
                    self.addFolderToZip(Zip_file, file)
        Zip_file.close()

    def addFolderToZip(self, Zip_file, folder): 
        for file in os.listdir(folder):
            full_path = os.path.join(folder, file)
            if os.path.isfile(full_path):
                print 'File added: ' + str(full_path)
                Zip_file.write(full_path)
            Elif os.path.isdir(full_path):
                print 'Entering folder: ' + str(full_path)
                self.addFolderToZip(Zip_file, full_path)

def main():
    utilities = ZipUtilities()
    filename = 'TEMP.Zip'
    directory = 'TEMP'
    utilities.toZip(directory, filename)

main()

Je suis entrain de courir:

python tozip.py

Ceci est le journal:

havok@fireshield:~$ python tozip.py

File added: TEMP/NARF (7ª copia)
Entering folder: TEMP/TEMP2
File added: TEMP/TEMP2/NERF (otra copia)
File added: TEMP/TEMP2/NERF (copia)
File added: TEMP/TEMP2/NARF
File added: TEMP/TEMP2/NARF (copia)
File added: TEMP/TEMP2/NARF (otra copia)
Entering folder: TEMP/TEMP2/TEMP3
File added: TEMP/TEMP2/TEMP3/DOCUMENTO DEL FINAL
File added: TEMP/TEMP2/TEMP3/DOCUMENTO DEL FINAL (copia)
File added: TEMP/TEMP2/NERF
File added: TEMP/NARF (copia) (otra copia)
File added: TEMP/NARF (copia) (copia)
File added: TEMP/NARF (6ª copia)
File added: TEMP/NERF (copia) (otra copia)
File added: TEMP/NERF (4ª copia)
File added: TEMP/NERF (otra copia)
File added: TEMP/NERF (3ª copia)
File added: TEMP/NERF (6ª copia)
File added: TEMP/NERF (copia)
File added: TEMP/NERF (5ª copia)
File added: TEMP/NARF (8ª copia)
File added: TEMP/NARF (3ª copia)
File added: TEMP/NARF (5ª copia)
File added: TEMP/NERF (copia) (3ª copia)
File added: TEMP/NARF
File added: TEMP/NERF (copia) (copia)
File added: TEMP/NERF (8ª copia)
File added: TEMP/NERF (7ª copia)
File added: TEMP/NARF (copia)
File added: TEMP/NARF (otra copia)
File added: TEMP/NARF (4ª copia)
File added: TEMP/NERF
File added: TEMP/NARF (copia) (3ª copia)

Comme vous pouvez le constater, cela fonctionne, les archives sont bien aussi. C'est une fonction récursive qui permet de compresser un dossier entier. Le seul problème est qu'il ne crée pas de dossier vide.

À votre santé.

8
havok-cr

Vous trouverez ci-dessous un code permettant de compresser un répertoire entier dans un fichier zip.

Cela semble fonctionner correctement pour créer des fichiers Zip sous Windows et Linux. Les fichiers de sortie Semblent s’extraire correctement sous Windows (fonctionnalité Dossiers compressés intégrée, WinZip et 7-Zip) et sous Linux. Cependant, les répertoires vides dans un fichier Zip apparaissent. Le problème est épineux. La solution ci-dessous semble fonctionner, mais le résultat de "Zipinfo" sur Linux est préoccupant. De plus, les autorisations de répertoire ne sont pas définies correctement pour les répertoires vides de l’archive Zip. Cela semble nécessiter des recherches plus approfondies.

J'ai eu quelques infos de ce fil de critiques de vélocité et ce fil de liste de diffusion python .

Notez que cette fonction est conçue pour placer des fichiers dans l’archive Zip avec Aucun répertoire parent ou un seul répertoire parent. Ainsi, tous les répertoires Principaux sont supprimés des chemins de système de fichiers sans être inclus dans le Zip archive des chemins. C'est généralement le cas lorsque vous voulez simplement prendre un répertoire d et le transformer en un fichier Zip pouvant être extrait à différents emplacements

Arguments de mots clés:

dirPath - chaîne du chemin du répertoire à archiver. Ceci est le seul argument requis. Il peut être absolu ou relatif, mais seulement un ou zéro Répertoires principaux seront inclus dans l’archive Zip.

zipFilePath - chemin de la chaîne vers le fichier Zip de sortie. Cela peut être un chemin absolu ou relatif. Si le fichier Zip existe déjà, il sera mis à jour. Si Pas, il sera créé. Si vous souhaitez le remplacer à partir de zéro, supprimez-le Avant d'appeler cette fonction. (la valeur par défaut est calculée sous la forme dirPath + ".Zip")

includeDirInZip - booléen indiquant si le répertoire de niveau supérieur doit être inclus ou non dans l'archive. (True par défaut)

(Notez que StackOverflow semble échouer pour imprimer mon python avec Des chaînes triées de guillemets, je viens donc de convertir mes chaînes doc en texte post ici)

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

def zipdir(dirPath=None, zipFilePath=None, includeDirInZip=True):

    if not zipFilePath:
        zipFilePath = dirPath + ".Zip"
    if not os.path.isdir(dirPath):
        raise OSError("dirPath argument must point to a directory. "
            "'%s' does not." % dirPath)
    parentDir, dirToZip = os.path.split(dirPath)
    #Little nested function to prepare the proper archive path
    def trimPath(path):
        archivePath = path.replace(parentDir, "", 1)
        if parentDir:
            archivePath = archivePath.replace(os.path.sep, "", 1)
        if not includeDirInZip:
            archivePath = archivePath.replace(dirToZip + os.path.sep, "", 1)
        return os.path.normcase(archivePath)

    outFile = zipfile.ZipFile(zipFilePath, "w",
        compression=zipfile.Zip_DEFLATED)
    for (archiveDirPath, dirNames, fileNames) in os.walk(dirPath):
        for fileName in fileNames:
            filePath = os.path.join(archiveDirPath, fileName)
            outFile.write(filePath, trimPath(filePath))
        #Make sure we get empty directories as well
        if not fileNames and not dirNames:
            zipInfo = zipfile.ZipInfo(trimPath(archiveDirPath) + "/")
            #some web sites suggest doing
            #zipInfo.external_attr = 16
            #or
            #zipInfo.external_attr = 48
            #Here to allow for inserting an empty directory.  Still TBD/TODO.
            outFile.writestr(zipInfo, "")
    outFile.close()

Voici quelques exemples d'utilisations. Notez que si votre argument dirPath comporte plusieurs répertoires principaux, seul le dernier est inclus par défaut. Passez includeDirInZip = False pour omettre tous les principaux répertoires.

zipdir("foo") #Just give it a dir and get a .Zip file
zipdir("foo", "foo2.Zip") #Get a .Zip file with a specific file name
zipdir("foo", "foo3nodir.Zip", False) #Omit the top level directory
zipdir("../test1/foo", "foo4nopardirs.Zip")
4
Peter Lyons

voici ma fonction que j'utilise pour compresser un dossier:

import os
import os.path
import zipfile

def Zip_dir(dirpath, zippath):
    fzip = zipfile.ZipFile(zippath, 'w', zipfile.Zip_DEFLATED)
    basedir = os.path.dirname(dirpath) + '/' 
    for root, dirs, files in os.walk(dirpath):
        if os.path.basename(root)[0] == '.':
            continue #skip hidden directories        
        dirname = root.replace(basedir, '')
        for f in files:
            if f[-1] == '~' or (f[0] == '.' and f != '.htaccess'):
                #skip backup files and all hidden files except .htaccess
                continue
            fzip.write(root + '/' + f, dirname + '/' + f)
    fzip.close()
3
Dmitry Nedbaylo

Si vous regardez un fichier Zip créé avec Info-Zip, vous verrez que les répertoires sont bien listés:

$ Zip foo.Zip -r foo
  adding: foo/ (stored 0%)
  adding: foo/foo.jpg (deflated 84%)
$ less foo.Zip
  Archive:  foo.Zip
 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
--------  ------  ------- ---- ---------- ----- --------  ----
       0  Stored        0   0% 2013-08-18 14:32 00000000  foo/
  476320  Defl:N    77941  84% 2013-08-18 14:31 55a52268  foo/foo.jpg
--------          -------  ---                            -------
  476320            77941  84%                            2 files

Notez que l'entrée de répertoire a une longueur nulle et n'est pas compressée. Il semble que vous puissiez obtenir le même résultat avec Python en écrivant le répertoire par son nom mais en le forçant à ne pas utiliser la compression.

if os.path.isdir(name):
    zf.write(name, arcname=arcname, compress_type=zipfile.Zip_STORED)
else:
    zf.write(name, arcname=arcname, compress_type=zipfile.Zip_DEFLATED)

Cela vaut peut-être la peine de s’assurer que arcname se termine par un /.

2
z0r

après avoir ajouté quelques importations, votre code fonctionne correctement pour moi. Comment appelez-vous le script? Peut-être pourriez-vous nous indiquer la structure des dossiers du répertoire '..\emballé\bin'. 

J'ai appelé votre code avec les arguments suivants:

planName='test.Zip'
files=['z.py',]
folders=['c:\\temp']
(success,filename)=createZipFile(planName,files,folders)

`

2
RSabet

Merci beaucoup pour cette fonction utile! Je l'ai trouvé très utile car je cherchais également de l'aide. Cependant, il serait peut-être utile de le modifier un peu pour que 

basedir = os.path.dirname(dirpath) + '/'

serait 

basedir = os.path.dirname(dirpath + '/')

Parce que trouvé que si je veux Zip dossier 'Example' qui se trouve à 'C:\folder\path\notWanted\to\Zip\Example',

Je suis dans Windows:

dirpath = 'C:\folder\path\notWanted\to\Zip\Example'
basedir = 'C:\folder\path\notWanted\to\Zip\Example/'
dirname = 'C:\folder\path\notWanted\to\Zip\Example\Example\Subfolder_etc'

Mais je suppose que votre code devrait donner

dirpath = 'C:\folder\path\notWanted\to\Zip\Example'
basedir = 'C:\folder\path\notWanted\to\Zip\Example\'
dirname = '\Subfolder_etc'
0
note
import os
import zipfile

zf = zipfile.ZipFile("file.Zip", "w")
for file in os.listdir(os.curdir):
    if not file.endswith('.Zip') and os.path.isfile(os.curdir+'/'+file):
        print file
        zf.write(file)
    Elif os.path.isdir(os.curdir+'/'+file):
        print f
        for f in os.listdir(os.curdir+'/'+file):
            zf.write(file+'\\'+f)
zf.close()
0
sohom

Heres le code édité que j'ai couru. Son basé sur le code ci-dessus, tiré de la liste de diffusion. J'ai ajouté les importations et fait une routine principale. J'ai également coupé les manipulations avec le nom du fichier de sortie pour raccourcir le code.

#!/usr/bin/env python

import os, zipfile, glob, sys

def addFolderToZip(myZipFile,folder):
    folder = folder.encode('ascii') #convert path to ascii for ZipFile Method
    for file in glob.glob(folder+"/*"):
            if os.path.isfile(file):
                print file
                myZipFile.write(file, os.path.basename(file), zipfile.Zip_DEFLATED)
            Elif os.path.isdir(file):
                addFolderToZip(myZipFile,file)

def createZipFile(filename,files,folders):
    myZipFile = zipfile.ZipFile( filename, "w" ) # Open the Zip file for writing 
    for file in files:
        file = file.encode('ascii') #convert path to ascii for ZipFile Method
        if os.path.isfile(file):
            (filepath, filename) = os.path.split(file)
            myZipFile.write( file, filename, zipfile.Zip_DEFLATED )

    for folder in  folders:   
        addFolderToZip(myZipFile,folder)  
    myZipFile.close()
    return (1,filename)

if __name__=="__main__":
    #put everything in sys.argv[1] in out.Zip, skip files
    print createZipFile("out.Zip", [], sys.argv[1])

Au travail, sur ma machine Windows, ce code fonctionnait bien mais ne créait aucun "dossier" dans le fichier zip. Au moins je m'en souviens. Maintenant à la maison, sur ma machine Linux, le fichier Zip créé semble être mauvais:

$ unzip -l out.Zip 
Archive:  out.Zip
  End-of-central-directory signature not found.  Either this file is not
  a zipfile, or it constitutes one disk of a multi-part archive.  In the
  latter case the central directory and zipfile comment will be found on
  the last disk(s) of this archive.
unzip:  cannot find zipfile directory in one of out.Zip or
        out.Zip.zip, and cannot find out.Zip.ZIP, period.

Je ne sais pas si j'ai violé accidentellement le code, je pense que c'est la même chose. Problèmes de plateforme croisée? De toute façon, ce n'est pas lié à ma question initiale; obtenir des dossiers dans le fichier Zip. Je voulais simplement publier le code que j'ai réellement exécuté, pas le code sur lequel je me suis fondé.

0
Mizipzor