Comment compresser un dossier entier en utilisant PHP


132

J'ai trouvé ici chez stackoveflow des codes sur la façon de compresser un fichier spécifique, mais qu'en est-il d'un dossier spécifique?

Folder/
  index.html
  picture.jpg
  important.txt

à l'intérieur My Folder, il y a des fichiers. après avoir compressé le My Folder, je veux également supprimer tout le contenu du dossier, sauf important.txt.

Trouvé ceci ici à la pile

J'ai besoin de ton aide. Merci.


Pour autant que je puisse voir, le lien stackoverflow que vous avez fourni compresser plusieurs fichiers. Avec quelle partie avez-vous des problèmes?
Lasse Espeholt

@lasseespeholt Le lien que je vous ai donné zippe uniquement un fichier spécifique, pas le dossier et le contenu du dossier ..
woninana

Il prend un tableau de fichiers (essentiellement un dossier) et ajoute tous les fichiers au fichier zip (la boucle). Je peux voir qu'une bonne réponse a été postée maintenant +1 :) qui est le même code, le tableau est juste une liste de fichiers d'un répertoire maintenant.
Lasse Espeholt


Réponses:


321

Code mis à jour le 22/04/2015.

Zip un dossier entier:

// Get real path for our folder
$rootPath = realpath('folder-to-zip');

// Initialize archive object
$zip = new ZipArchive();
$zip->open('file.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);

// Create recursive directory iterator
/** @var SplFileInfo[] $files */
$files = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($rootPath),
    RecursiveIteratorIterator::LEAVES_ONLY
);

foreach ($files as $name => $file)
{
    // Skip directories (they would be added automatically)
    if (!$file->isDir())
    {
        // Get real and relative path for current file
        $filePath = $file->getRealPath();
        $relativePath = substr($filePath, strlen($rootPath) + 1);

        // Add current file to archive
        $zip->addFile($filePath, $relativePath);
    }
}

// Zip archive will be created only after closing object
$zip->close();

Compressez un dossier entier + supprimez tous les fichiers sauf "important.txt":

// Get real path for our folder
$rootPath = realpath('folder-to-zip');

// Initialize archive object
$zip = new ZipArchive();
$zip->open('file.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);

// Initialize empty "delete list"
$filesToDelete = array();

// Create recursive directory iterator
/** @var SplFileInfo[] $files */
$files = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($rootPath),
    RecursiveIteratorIterator::LEAVES_ONLY
);

foreach ($files as $name => $file)
{
    // Skip directories (they would be added automatically)
    if (!$file->isDir())
    {
        // Get real and relative path for current file
        $filePath = $file->getRealPath();
        $relativePath = substr($filePath, strlen($rootPath) + 1);

        // Add current file to archive
        $zip->addFile($filePath, $relativePath);

        // Add current file to "delete list"
        // delete it later cause ZipArchive create archive only after calling close function and ZipArchive lock files until archive created)
        if ($file->getFilename() != 'important.txt')
        {
            $filesToDelete[] = $filePath;
        }
    }
}

// Zip archive will be created only after closing object
$zip->close();

// Delete all files from "delete list"
foreach ($filesToDelete as $file)
{
    unlink($file);
}

2
Vous devez définir chmod (accessible en écriture) sur dir (où se trouve ce script) sur 777. Par exemple: Si le script se trouve dans /var/www/localhost/script.php, vous devez définir chmod 0777 sur dir / var / www / localhost /.
Dador

3
La suppression des fichiers avant l'appel $zip->close()ne fonctionnera pas. Vérifiez ma réponse ici
hek2mgl

10
@alnassre c'est l'exigence de la question: "Je veux aussi supprimer tout le contenu du dossier sauf important.txt". Je vous conseille également de toujours lire le code avant de l'exécuter.
Dador

1
@alnassre hahahaha ... sorry :) ... hahaha
Ondrej Rafaj

1
@ nick-newman, ouais, pour calculer le pourcentage, vous pouvez utiliser php.net/manual/ru/function.iterator-count.php + counter inside loop. Concernant le niveau de compression - ce n'est pas possible avec ZipArchive pour le moment: stackoverflow.com/questions/1833168/...
Dador

54

Il existe une méthode utile non documentée dans la classe ZipArchive: addGlob ();

$zipFile = "./testZip.zip";
$zipArchive = new ZipArchive();

if ($zipArchive->open($zipFile, (ZipArchive::CREATE | ZipArchive::OVERWRITE)) !== true)
    die("Failed to create archive\n");

$zipArchive->addGlob("./*.txt");
if ($zipArchive->status != ZIPARCHIVE::ER_OK)
    echo "Failed to write files to zip\n";

$zipArchive->close();

Maintenant documenté sur: www.php.net/manual/en/ziparchive.addglob.php


2
@netcoder - les avantages d'avoir écrit le phpt pour le tester ... en gros, lisez le source de la classe ZipArchive et l'avez trouvé là-bas .... il y a aussi une méthode addPattern () non documentée qui prend un modèle de style regexp, mais je n'ai jamais réussi à faire fonctionner cela (peut-être un bogue dans la classe)
Mark Baker

1
@kread - vous pouvez l'utiliser avec n'importe quelle liste de fichiers pouvant être extraite à l'aide de glob (), donc je l'ai trouvé extrêmement utile depuis que je l'ai découvert.
Mark Baker

@MarkBaker Je sais que ce commentaire traverse des années après votre publication, je tente juste ma chance ici. J'ai posté une question ici au sujet de passer comme un éclair aussi bien. Je suis sur le point d'essayer la méthode glob que vous avez publiée ici, mais mon principal problème est que je ne peux pas utiliser addFromString et que j'ai utilisé addFile, qui échoue simplement en silence. Avez-vous peut-être une idée de ce qui pourrait mal se passer ou de ce que je pourrais faire de mal?
Skytiger

@ user1032531 - la dernière ligne de mon message (édité le 13 décembre 2013) indique exactement cela, avec un lien vers la page de la documentation
Mark Baker

6
Est addGlobrécursif?
Vincent Poirier

20

Essaye ça:

$zip = new ZipArchive;
$zip->open('myzip.zip', ZipArchive::CREATE);
foreach (glob("target_folder/*") as $file) {
    $zip->addFile($file);
    if ($file != 'target_folder/important.txt') unlink($file);
}
$zip->close();

Ce ne sera pas zip récursive cependant.


Cela supprime certains fichiers My folder, mais j'ai aussi un dossier dans un dossier My folderqui me donne une erreur de: Autorisation refusée en dissociant le dossier avec inMy folder
woninana

@Stupefy: Essayez à la if (!is_dir($file) && $file != 'target_folder...')place. Ou cochez la réponse @kread si vous souhaitez compresser récursivement, c'est le moyen le plus efficace.
netcoder

Le dossier dans le My foldern'est toujours pas supprimé, mais il n'y a quand même plus d'erreurs.
woninana le

J'ai également oublié de mentionner qu'il n'y a pas de fichiers .zip créés.
woninana le

1
La suppression des fichiers avant l'appel $zip->close()ne fonctionnera pas. Vérifiez ma réponse ici
hek2mgl

19

Je suppose que cela fonctionne sur un serveur où l'application zip est dans le chemin de recherche. Cela devrait être vrai pour tous les serveurs basés sur Unix et je suppose que la plupart des serveurs Windows.

exec('zip -r archive.zip "My folder"');
unlink('My\ folder/index.html');
unlink('My\ folder/picture.jpg');

L'archive résidera ensuite dans archive.zip. Gardez à l'esprit que les espaces vides dans les noms de fichiers ou de dossiers sont une cause fréquente d'erreurs et doivent être évités dans la mesure du possible.


15

J'ai essayé avec le code ci-dessous et cela fonctionne. Le code est explicite, veuillez me le faire savoir si vous avez des questions.

<?php
class FlxZipArchive extends ZipArchive 
{
 public function addDir($location, $name) 
 {
       $this->addEmptyDir($name);
       $this->addDirDo($location, $name);
 } 
 private function addDirDo($location, $name) 
 {
    $name .= '/';
    $location .= '/';
    $dir = opendir ($location);
    while ($file = readdir($dir))
    {
        if ($file == '.' || $file == '..') continue;
        $do = (filetype( $location . $file) == 'dir') ? 'addDir' : 'addFile';
        $this->$do($location . $file, $name . $file);
    }
 } 
}
?>

<?php
$the_folder = '/path/to/folder/to/be/zipped';
$zip_file_name = '/path/to/zip/archive.zip';
$za = new FlxZipArchive;
$res = $za->open($zip_file_name, ZipArchive::CREATE);
if($res === TRUE) 
{
    $za->addDir($the_folder, basename($the_folder));
    $za->close();
}
else{
echo 'Could not create a zip archive';
}
?>

Excellente solution. Cela fonctionne aussi dans laravel 5.5. vraiment aimé ça. (y)
Web Artisan

1
Excellent code! Propre, simple et fonctionne parfaitement! ;) Cela me semble la meilleure réponse. Si cela peut aider quelqu'un: je viens d'ajouter ini_set('memory_limit', '512M');avant l'exécution du script et ini_restore('memory_limit');à la fin. Il fallait éviter le manque de mémoire en cas de dossiers lourds (c'était un dossier de plus de 500 Mo).
Jacopo Pace

1
Dans mon environnement (PHP 7.3, Debian), une archive ZIP sans liste de répertoires a été créée (gros fichier vide). J'ai dû changer la ligne suivante: $ name. = '/'; dans $ name = ($ name == '.'? '': $ name. '/');
Gerfried

Cela fonctionne pour moi. Merci d'avoir partagé. À votre santé!
Sathiska le

8

C'est une fonction qui zippe un dossier entier et son contenu dans un fichier zip et vous pouvez l'utiliser simplement comme ceci:

addzip ("path/folder/" , "/path2/folder.zip" );

fonction :

// compress all files in the source directory to destination directory 
    function create_zip($files = array(), $dest = '', $overwrite = false) {
    if (file_exists($dest) && !$overwrite) {
        return false;
    }
    if (($files)) {
        $zip = new ZipArchive();
        if ($zip->open($dest, $overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) {
            return false;
        }
        foreach ($files as $file) {
            $zip->addFile($file, $file);
        }
        $zip->close();
        return file_exists($dest);
    } else {
        return false;
    }
}

function addzip($source, $destination) {
    $files_to_zip = glob($source . '/*');
    create_zip($files_to_zip, $destination);
    echo "done";
}

Comment inclure automatiquement des sous-dossiers dans la sauvegarde avec ce script? @Alireza
floCoder

2

Pourquoi ne pas essayer EFS PhP-ZiP MultiVolume Script ... J'ai compressé et transféré des centaines de concerts et des millions de fichiers ... ssh est nécessaire pour créer efficacement des archives.

Mais je crois que les fichiers résultants peuvent être utilisés avec exec directement à partir de php:

exec('zip -r backup-2013-03-30_0 . -i@backup-2013-03-30_0.txt');

Je ne sais pas si ça marche. Je n'ai pas essayé ...

"le secret" est que le temps d'exécution de l'archivage ne doit pas dépasser le temps imparti pour l'exécution du code PHP.


1

Voici un exemple de travail de création de ZIP en PHP:

$zip = new ZipArchive();
$zip_name = time().".zip"; // Zip name
$zip->open($zip_name,  ZipArchive::CREATE);
foreach ($files as $file) {
  echo $path = "uploadpdf/".$file;
  if(file_exists($path)){
  $zip->addFromString(basename($path),  file_get_contents($path));---This is main function  
  }
  else{
   echo"file does not exist";
  }
}
$zip->close();

1

J'ai trouvé cet article dans google comme deuxième meilleur résultat, le premier utilisait exec :(

Quoi qu'il en soit, même si cela ne correspondait pas exactement à mes besoins .. J'ai décidé de publier une réponse pour les autres avec ma version rapide mais étendue de cela.

CARACTÉRISTIQUES DU SCRIPT

  • Nom du fichier de sauvegarde au jour le jour, PREFIX-YYYY-MM-DD-POSTFIX.EXTENSION
  • Rapport de fichier / manquant
  • Liste des sauvegardes précédentes
  • Ne compresse pas / n'inclut pas les sauvegardes précédentes;)
  • Fonctionne sous Windows / Linux

Quoi qu'il en soit, sur le script .. Bien que cela puisse paraître beaucoup .. N'oubliez pas qu'il y a un excès ici .. Alors n'hésitez pas à supprimer les sections de rapport si nécessaire ...

De plus, cela peut aussi sembler désordonné et certaines choses pourraient être nettoyées facilement ... Alors ne commentez pas à ce sujet, c'est juste un script rapide avec des commentaires de base ajoutés .. PAS POUR UNE UTILISATION EN DIRECT .. Mais facile à nettoyer pour une utilisation en direct !

Dans cet exemple, il est exécuté à partir d'un répertoire qui se trouve à l'intérieur du dossier racine www / public_html .. Il suffit donc de remonter un dossier pour accéder à la racine.

<?php
    // DIRECTORY WE WANT TO BACKUP
    $pathBase = '../';  // Relate Path

    // ZIP FILE NAMING ... This currently is equal to = sitename_www_YYYY_MM_DD_backup.zip 
    $zipPREFIX = "sitename_www";
    $zipDATING = '_' . date('Y_m_d') . '_';
    $zipPOSTFIX = "backup";
    $zipEXTENSION = ".zip";

    // SHOW PHP ERRORS... REMOVE/CHANGE FOR LIVE USE
    ini_set('display_errors',1);
    ini_set('display_startup_errors',1);
    error_reporting(-1);




// ############################################################################################################################
//                                  NO CHANGES NEEDED FROM THIS POINT
// ############################################################################################################################

    // SOME BASE VARIABLES WE MIGHT NEED
    $iBaseLen = strlen($pathBase);
    $iPreLen = strlen($zipPREFIX);
    $iPostLen = strlen($zipPOSTFIX);
    $sFileZip = $pathBase . $zipPREFIX . $zipDATING . $zipPOSTFIX . $zipEXTENSION;
    $oFiles = array();
    $oFiles_Error = array();
    $oFiles_Previous = array();

    // SIMPLE HEADER ;)
    echo '<center><h2>PHP Example: ZipArchive - Mayhem</h2></center>';

    // CHECK IF BACKUP ALREADY DONE
    if (file_exists($sFileZip)) {
        // IF BACKUP EXISTS... SHOW MESSAGE AND THATS IT
        echo "<h3 style='margin-bottom:0px;'>Backup Already Exists</h3><div style='width:800px; border:1px solid #000;'>";
            echo '<b>File Name: </b>',$sFileZip,'<br />';
            echo '<b>File Size: </b>',$sFileZip,'<br />';
        echo "</div>";
        exit; // No point loading our function below ;)
    } else {

        // NO BACKUP FOR TODAY.. SO START IT AND SHOW SCRIPT SETTINGS
        echo "<h3 style='margin-bottom:0px;'>Script Settings</h3><div style='width:800px; border:1px solid #000;'>";
            echo '<b>Backup Directory: </b>',$pathBase,'<br /> ';
            echo '<b>Backup Save File: </b>',$sFileZip,'<br />';
        echo "</div>";

        // CREATE ZIPPER AND LOOP DIRECTORY FOR SUB STUFF
        $oZip = new ZipArchive;
        $oZip->open($sFileZip,  ZipArchive::CREATE | ZipArchive::OVERWRITE);
        $oFilesWrk = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($pathBase),RecursiveIteratorIterator::LEAVES_ONLY);
        foreach ($oFilesWrk as $oKey => $eFileWrk) {
            // VARIOUS NAMING FORMATS OF THE CURRENT FILE / DIRECTORY.. RELATE & ABSOLUTE
            $sFilePath = substr($eFileWrk->getPathname(),$iBaseLen, strlen($eFileWrk->getPathname())- $iBaseLen);
            $sFileReal = $eFileWrk->getRealPath();
            $sFile = $eFileWrk->getBasename();

            // WINDOWS CORRECT SLASHES
            $sMyFP = str_replace('\\', '/', $sFileReal);

            if (file_exists($sMyFP)) {  // CHECK IF THE FILE WE ARE LOOPING EXISTS
                if ($sFile!="."  && $sFile!="..") { // MAKE SURE NOT DIRECTORY / . || ..
                    // CHECK IF FILE HAS BACKUP NAME PREFIX/POSTFIX... If So, Dont Add It,, List It
                    if (substr($sFile,0, $iPreLen)!=$zipPREFIX && substr($sFile,-1, $iPostLen + 4)!= $zipPOSTFIX.$zipEXTENSION) {
                        $oFiles[] = $sMyFP;                     // LIST FILE AS DONE
                        $oZip->addFile($sMyFP, $sFilePath);     // APPEND TO THE ZIP FILE
                    } else {
                        $oFiles_Previous[] = $sMyFP;            // LIST PREVIOUS BACKUP
                    }
                }
            } else {
                $oFiles_Error[] = $sMyFP;                       // LIST FILE THAT DOES NOT EXIST
            }
        }
        $sZipStatus = $oZip->getStatusString();                 // GET ZIP STATUS
        $oZip->close(); // WARNING: Close Required to append files, dont delete any files before this.

        // SHOW BACKUP STATUS / FILE INFO
        echo "<h3 style='margin-bottom:0px;'>Backup Stats</h3><div style='width:800px; height:120px; border:1px solid #000;'>";
            echo "<b>Zipper Status: </b>" . $sZipStatus . "<br />";
            echo "<b>Finished Zip Script: </b>",$sFileZip,"<br />";
            echo "<b>Zip Size: </b>",human_filesize($sFileZip),"<br />";
        echo "</div>";


        // SHOW ANY PREVIOUS BACKUP FILES
        echo "<h3 style='margin-bottom:0px;'>Previous Backups Count(" . count($oFiles_Previous) . ")</h3><div style='overflow:auto; width:800px; height:120px; border:1px solid #000;'>";
        foreach ($oFiles_Previous as $eFile) {
            echo basename($eFile) . ", Size: " . human_filesize($eFile) . "<br />";
        }
        echo "</div>";

        // SHOW ANY FILES THAT DID NOT EXIST??
        if (count($oFiles_Error)>0) {
            echo "<h3 style='margin-bottom:0px;'>Error Files, Count(" . count($oFiles_Error) . ")</h3><div style='overflow:auto; width:800px; height:120px; border:1px solid #000;'>";
            foreach ($oFiles_Error as $eFile) {
                echo $eFile . "<br />";
            }
            echo "</div>";
        }

        // SHOW ANY FILES THAT HAVE BEEN ADDED TO THE ZIP
        echo "<h3 style='margin-bottom:0px;'>Added Files, Count(" . count($oFiles) . ")</h3><div style='overflow:auto; width:800px; height:120px; border:1px solid #000;'>";
        foreach ($oFiles as $eFile) {
            echo $eFile . "<br />";
        }
        echo "</div>";

    }


    // CONVERT FILENAME INTO A FILESIZE AS Bytes/Kilobytes/Megabytes,Giga,Tera,Peta
    function human_filesize($sFile, $decimals = 2) {
        $bytes = filesize($sFile);
        $sz = 'BKMGTP';
        $factor = floor((strlen($bytes) - 1) / 3);
        return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
    }
?>

QU'EST CE QUE ÇA FAIT??

Il compressera simplement le contenu complet de la variable $ pathBase et stockera le zip dans ce même dossier. Il effectue une détection simple des sauvegardes précédentes et les ignore.

SAUVEGARDE CRON

Ce script que je viens de tester sur Linux et a bien fonctionné à partir d'un travail cron en utilisant une URL absolue pour la base de chemin.


J'ai également exclu le script de suppression, vous pouvez voir la réponse acceptée pour cela
Angry 84

Je dois aimer ces votes aléatoires sans commentaire expliquant pourquoi.
Angry 84

1

Utilisez cette fonction:

function zip($source, $destination)
{
    if (!extension_loaded('zip') || !file_exists($source)) {
        return false;
    }

    $zip = new ZipArchive();
    if (!$zip->open($destination, ZIPARCHIVE::CREATE)) {
        return false;
    }

    $source = str_replace('\\', '/', realpath($source));

    if (is_dir($source) === true) {
        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);

        foreach ($files as $file) {
            $file = str_replace('\\', '/', $file);

            // Ignore "." and ".." folders
            if (in_array(substr($file, strrpos($file, '/')+1), array('.', '..'))) {
                continue;
            }               

            $file = realpath($file);

            if (is_dir($file) === true) {
                $zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
            } elseif (is_file($file) === true) {
                $zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
            }
        }
    } elseif (is_file($source) === true) {
        $zip->addFromString(basename($source), file_get_contents($source));
    }

    return $zip->close();
}

Exemple d'utilisation:

zip('/folder/to/compress/', './compressed.zip');

1

Utilisez cela fonctionne bien.

$dir = '/Folder/';
$zip = new ZipArchive();
$res = $zip->open(trim($dir, "/") . '.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
if ($res === TRUE) {
    foreach (glob($dir . '*') as $file) {
        $zip->addFile($file, basename($file));
    }
    $zip->close();
} else {
    echo 'Failed to create to zip. Error: ' . $res;
}

1

Créez un dossier zip en PHP.

Zip create méthode

   public function zip_creation($source, $destination){
    $dir = opendir($source);
    $result = ($dir === false ? false : true);

    if ($result !== false) {

        
        $rootPath = realpath($source);
         
        // Initialize archive object
        $zip = new ZipArchive();
        $zipfilename = $destination.".zip";
        $zip->open($zipfilename, ZipArchive::CREATE | ZipArchive::OVERWRITE );
         
        // Create recursive directory iterator
        /** @var SplFileInfo[] $files */
        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($rootPath), RecursiveIteratorIterator::LEAVES_ONLY);
         
        foreach ($files as $name => $file)
        {
            // Skip directories (they would be added automatically)
            if (!$file->isDir())
            {
                // Get real and relative path for current file
                $filePath = $file->getRealPath();
                $relativePath = substr($filePath, strlen($rootPath) + 1);
         
                // Add current file to archive
                $zip->addFile($filePath, $relativePath);
            }
        }
         
        // Zip archive will be created only after closing object
        $zip->close();
        
        return TRUE;
    } else {
        return FALSE;
    }


}

Appelez la méthode zip

$source = $source_directory;
$destination = $destination_directory;
$zipcreation = $this->zip_creation($source, $destination);

0

J'ai fait quelques petites améliorations dans le script.

  <?php
    $directory = "./";
    //create zip object
    $zip = new ZipArchive();
    $zip_name = time().".zip";
    $zip->open($zip_name,  ZipArchive::CREATE);
    $files = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($directory),
        RecursiveIteratorIterator::LEAVES_ONLY
    );
    foreach ($files as $file) {
        $path = $file->getRealPath();
        //check file permission
        if(fileperms($path)!="16895"){
            $zip->addFromString(basename($path),  file_get_contents($path)) ;
            echo "<span style='color:green;'>{$path} is added to zip file.<br /></span> " ;
        }
        else{
            echo"<span style='color:red;'>{$path} location could not be added to zip<br /></span>";
        }
    }
    $zip->close();
    ?>

Ceci fait compresser les fichiers mais la liste des répertoires a disparu, il n'y a plus de répertoire
Sujay sreedhar

0

Cela résoudra votre problème. Essayez-le.

$zip = new ZipArchive;
$zip->open('testPDFZip.zip', ZipArchive::CREATE);
foreach (glob(APPLICATION_PATH."pages/recruitment/uploads/test_pdf_folder/*") as $file) {
    $new_filename = end(explode("/",$file));
    $zip->addFile($file,"emp/".$new_filename);
}           
$zip->close();

0

Pour tous ceux qui lisent cet article et recherchent pourquoi compresser les fichiers en utilisant addFile au lieu de addFromString, qui ne compressent pas les fichiers avec leur chemin absolu (il suffit de compresser les fichiers et rien d'autre), voir ma question et ma réponse ici

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.