web-dev-qa-db-fra.com

Décompression de la structure du répertoire avec python

J'ai un fichier Zip qui contient la structure de répertoires suivante:

dir1\dir2\dir3a
dir1\dir2\dir3b

J'essaye de le décompresser et de maintenir la structure du répertoire mais j'obtiens l'erreur:

IOError: [Errno 2] No such file or directory: 'C:\\\projects\\\testFolder\\\subdir\\\unzip.exe'

où testFolder est dir1 ci-dessus et subdir est dir2.

Existe-t-il un moyen rapide de décompresser le fichier et de maintenir la structure du répertoire?

29
Flyer1

Les méthodes d'extraction et d'extraction sont excellentes si vous êtes sur Python 2.6. Je dois utiliser Python 2.5 pour l'instant, donc j'ai juste besoin de créer les répertoires s'ils n'existent pas. Vous pouvez obtenir une liste des répertoires avec la méthode namelist(). Les répertoires se termineront toujours par une barre oblique (même sous Windows), par exemple,

import os, zipfile

z = zipfile.ZipFile('myfile.Zip')
for f in z.namelist():
    if f.endswith('/'):
        os.makedirs(f)

Vous ne voulez probablement pas le faire exactement comme ça (c'est-à-dire que vous voudriez probablement extraire le contenu du fichier Zip lorsque vous parcourez la liste de noms), mais vous avez l'idée.

23
Jeff

Ne pas faire confiance à extract () ou extractall ().

Ces méthodes extraient aveuglément des fichiers vers les chemins d'accès indiqués dans leurs noms de fichiers. Mais les noms de fichiers Zip peuvent être n'importe quoi, y compris des chaînes dangereuses comme "x /../../../ etc/passwd". Extrayez de tels fichiers et vous pourriez avoir compromis tout votre serveur.

Peut-être que cela devrait être considéré comme une faille de sécurité à signaler dans le module zipfile de Python, mais un certain nombre de Zip-dearchivers ont montré le même comportement dans le passé. Pour désarchiver un fichier Zip avec une structure de dossiers en toute sécurité, vous devez vérifier en profondeur chaque chemin de fichier.

16
bobince

J'ai essayé cela et je peux le reproduire. La méthode extractall, comme suggéré par d'autres réponses, ne pas résout le problème. Cela me semble être un bogue dans le module zipfile (peut-être uniquement Windows?), À moins que je ne comprenne mal la structure des fichiers zip.

testa\
testa\testb\
testa\testb\test.log
> test.Zip

>>> from zipfile import ZipFile
>>> zipTest = ZipFile("C:\\...\\test.Zip")
>>> zipTest.extractall("C:\\...\\")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "...\zipfile.py", line 940, in extractall
  File "...\zipfile.py", line 928, in extract
  File "...\zipfile.py", line 965, in _extract_member
IOError: [Errno 2] No such file or directory: 'C:\\...\\testa\\testb\\test.log'

Si je fais une printdir(), j'obtiens ceci (première colonne):

>>> zipTest.printdir()
File Name
testa/testb/
testa/testb/test.log

Si j'essaye d'extraire juste la première entrée, comme ceci:

>>> zipTest.extract("testa/testb/")
'C:\\...\\testa\\testb'

Sur disque, cela se traduit par la création d'un dossier testa, avec un fichiertestb à l'intérieur. C'est apparemment la raison pour laquelle la tentative ultérieure d'extraire test.log Échoue; testa\testb Est un fichier, pas un dossier.

Edit # 1: Si vous extrayez uniquement le fichier, cela fonctionne:

>>> zipTest.extract("testa/testb/test.log")
'C:\\...\\testa\\testb\\test.log'

Edit # 2: le code de Jeff est le chemin à parcourir; parcourir namelist; s'il s'agit d'un répertoire, créez le répertoire. Sinon, extrayez le fichier.

8
DNS

Je sais qu'il est peut-être un peu tard pour le dire, mais Jeff a raison. C'est aussi simple que:

import os
from zipfile import ZipFile as Zip

def extractAll(zipName):
    z = Zip(zipName)
    for f in z.namelist():
        if f.endswith('/'):
            os.makedirs(f)
        else:
            z.extract(f)

if __name__ == '__main__':
    zipList = ['one.Zip', 'two.Zip', 'three.Zip']
    for Zip in zipList:
        extractAll(zipName)
6
ki113d

Il existe un moyen très simple si vous utilisez Python 2.6: la méthode extractall .

Cependant, puisque le module zipfile est implémenté complètement dans Python sans extension C, vous pouvez probablement le copier à partir d'une installation 2.6 et l'utiliser avec une ancienne version de Python; vous pouvez trouver cela plus facile que d'avoir à réimplémenter la fonctionnalité vous-même. Cependant, la fonction elle-même est assez courte:

def extractall(self, path=None, members=None, pwd=None):
    """Extract all members from the archive to the current working
       directory. `path' specifies a different directory to extract to.
       `members' is optional and must be a subset of the list returned
       by namelist().
    """
    if members is None:
        members = self.namelist()

    for zipinfo in members:
        self.extract(zipinfo, path, pwd)
3
Eli Courtwright

Filtrer la liste de noms pour exclure les dossiers

Tout ce que vous avez à faire est de filtrer les entrées namelist() finissant par / et le problème est résolu:

  z.extractall(dest, filter(lambda f: not f.endswith('/'), z.namelist()))

nJoy!

2
nickl-

Il semble que vous tentiez d'exécuter la décompression pour extraire le Zip.

Il serait préférable d'utiliser le module python zipfile , et donc de faire l'extraction en python.

import zipfile

def extract(zipfilepath, extractiondir):
    Zip = zipfile.ZipFile(zipfilepath)
    Zip.extractall(path=extractiondir)
2
Douglas Leeder

Si comme moi, vous devez extraire une archive Zip complète avec une ancienne version Python (dans mon cas, 2.4), voici ce que j'ai trouvé (basé sur la réponse de Jeff):

import zipfile
import os

def unzip(source_file_path, destination_dir):
    destination_dir += '/'
    z = zipfile.ZipFile(source_file_path, 'r')
    for file in z.namelist():
        outfile_path = destination_dir + file
        if file.endswith('/'):
            os.makedirs(outfile_path)
        else:
            outfile = open(outfile_path, 'wb')
            outfile.write(z.read(file))
            outfile.close()
    z.close()
2
Apteryx

Notez que les fichiers Zip peuvent avoir des entrées pour les répertoires ainsi que pour les fichiers. Lorsque vous créez des archives avec la commande Zip, passez la -D option pour désactiver l'ajout explicite d'entrées de répertoire à l'archive. Lorsque Python 2.6's ZipFile.extractall la méthode traverse une entrée de répertoire, elle semble créer un fichier à sa place. Étant donné que les entrées d'archive ne sont pas nécessairement en ordre, cela provoque ZipFile.extractall pour échouer assez souvent, car il essaie de créer un fichier dans un sous-répertoire d'un fichier. Si vous avez une archive que vous souhaitez utiliser avec le module Python, il suffit de l'extraire et de la recompresser avec le -D option. Voici un petit extrait que j'utilise depuis un certain temps pour faire exactement cela:

P=`pwd` && 
Z=`mktemp -d -t Zip` && 
pushd $Z && 
unzip $P/<busted>.Zip && 
Zip -r -D $P/<new>.Zip . && 
popd && 
rm -rf $Z

Remplacer <busted>.Zip et <new>.Zip avec de vrais noms de fichiers par rapport au répertoire courant. Ensuite, copiez le tout et collez-le dans un shell de commande, et cela créera une nouvelle archive prête à basculer avec Python 2.6. Il est a Zip commande qui supprimera ces entrées de répertoire sans décompresser mais IIRC il s'est comporté bizarrement dans différents environnements Shell ou configurations Zip.

1
xdissent