Question

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

dir1\dir2\dir3a
dir1\dir2\dir3b

J'essaie de le décompresser et de conserver la structure de répertoires, mais le message d'erreur suivant s'affiche:

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 conserver la structure de répertoires?

Était-ce utile?

La solution

Les méthodes extract et extractall sont très utiles si vous utilisez Python 2.6. Je dois utiliser Python 2.5 pour le moment, je dois donc simplement créer les répertoires s’ils n’existent pas. Vous pouvez obtenir une liste de 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 cela (c’est-à-dire que vous souhaitiez probablement extraire le contenu du fichier zip lorsque vous parcourez la liste de noms), mais vous obtenez le résultat. idée.

Autres conseils

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

Ces méthodes extraient aveuglément des fichiers dans les chemins indiqués dans leurs noms. Mais les noms de fichier ZIP peuvent être n'importe quoi, y compris des chaînes dangereuses comme & # 8220; x /../../../ etc / passwd & # 8221 ;. Extrayez de tels fichiers et vous auriez pu compromettre tout votre serveur.

Cela devrait peut-être être considéré comme une faille de sécurité à signaler dans le module zipfile de Python, mais un nombre illimité d’auteurs de recherche-zip ont eu exactement le même comportement dans le passé. Pour désarchiver un fichier ZIP avec une structure de dossiers en toute sécurité, une vérification approfondie de chaque chemin de fichier est nécessaire.

J'ai essayé ceci et je peux le reproduire. La méthode par extraction, suggérée par d’autres réponses, ne ne résout pas le problème. Cela ressemble à un bogue dans le module zipfile (peut-être uniquement sous Windows?), À moins que je ne comprenne mal comment les fichiers zip sont structurés.

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 un printdir(), j'obtiens ceci (première colonne):

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

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

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

Sur le disque, cela entraîne la création d'un dossier testa, contenant un fichier testb. C'est apparemment la raison pour laquelle la tentative d'extraction ultérieure test.log a échoué; testa\testb est un fichier, pas un dossier.

Modifier n ° 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 la voie à suivre; itérer à travers namelist; si c'est un répertoire, créez le répertoire. Sinon, extrayez le fichier.

Je sais qu'il est peut-être un peu tard pour dire cela, 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)

Si vous utilisez Python 2.6, il existe un moyen très simple: extraire méthode.

Cependant, étant donné que le module zipfile est entièrement implémenté dans Python sans aucune extension C, vous pouvez probablement le copier depuis une installation 2.6 et l’utiliser avec une version plus ancienne de Python; vous trouverez cela plus facile que de devoir réimplémenter vous-même la fonctionnalité. 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)

On dirait que vous essayez de décompresser le fichier pour l'extraire.

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

import zipfile

def extract(zipfilepath, extractiondir):
    zip = zipfile.ZipFile(zipfilepath)
    zip.extractall(path=extractiondir)

Liste de noms de filtre pour exclure les dossiers

Il suffit de filtrer les namelist() entrées se terminant par / et le problème est résolu:

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

nJoy!

Si comme moi, vous devez extraire une archive zip complète avec une version plus ancienne de Python (dans mon cas, la version 2.4), voici ce que j'ai proposé (d'après 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()

Notez que les fichiers zip peuvent avoir des entrées pour les répertoires ainsi que pour les fichiers. Lors de la création d'archives à l'aide de la commande zip, transmettez l'option -D pour désactiver l'ajout explicite d'entrées de répertoire à l'archive. Lorsque la méthode ZipFile.extractall de Python 2.6 s'exécute sur une entrée de répertoire, elle semble créer un fichier à la place . Etant donné que les entrées d’archive ne sont pas forcément dans l’ordre, <busted>.zip échoue fréquemment, car il essaie de créer un fichier dans un sous-répertoire d’un fichier. Si vous souhaitez utiliser une archive avec le module Python, extrayez-la simplement et resserrez-la à l'aide de l'option <new>.zip. Voici un petit extrait que j'utilise depuis longtemps 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

Remplacez <=> et <=> par des noms de fichiers réels par rapport au répertoire actuel. Il vous suffit ensuite de copier le tout et de le coller dans un interpréteur de commande, ce qui créera une nouvelle archive prête à fonctionner avec Python 2.6. Il a une <=> commande qui supprimera ces entrées de répertoire sans décompresser, mais le code IIRC s’est comporté de façon étrange dans différents environnements de shell ou configurations zip.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top