Copier le dossier de manière récursive dans node.js


155

Y at - il un moyen plus facile de copier un dossier et tout son contenu sans faire manuellement une séquence de fs.readir, fs.readfile, fs.writefilerécursive?

Je me demande simplement s'il me manque une fonction qui fonctionnerait idéalement comme ça

fs.copy("/path/to/source/folder","/path/to/destination/folder");

3
Existe-t-il un moyen de faire cela sans aucun module? Peut-être une fonction récursive / code snip-it?
Sukima

@Sukima - Voir ma réponse ici .
jmort253

Réponses:


121

Vous pouvez utiliser le module ncp . Je pense que c'est ce dont tu as besoin


2
Parfait! npm install ncpet travailler en moins de 30 secondes. Merci.
Aebsubis

1
La clé est meilleure pour moi, car elle prend en charge plus d'options. Avec NCP, vous ne pouvez pas résoudre les liens symboliques par exemple.
Slava Fomin II

3
En prime, on peut utiliser ncp dans un script d'exécution multiplateforme npm.
Ciantic

J'ai eu quelques cas simples où ncp ne va pas dans mon rappel alors que fs-extra le fait correctement.
bumpmann

40
Veuillez noter que ncp ne semble pas être maintenu . fs-extra est probablement la meilleure option à la place.
chris

74

C'est mon approche pour résoudre ce problème sans aucun module supplémentaire. En utilisant simplement les modules intégrés fset path.

Remarque: Cela utilise les fonctions de lecture / écriture de fs donc il ne copie aucune méta-donnée (heure de création, etc.). À partir du nœud 8.5, il existe une copyFileSyncfonction disponible qui appelle les fonctions de copie du système d'exploitation et copie donc également les métadonnées. Je ne les ai pas encore testés, mais cela devrait fonctionner de simplement les remplacer. (Voir https://nodejs.org/api/fs.html#fs_fs_copyfilesync_src_dest_flags )

var fs = require('fs');
var path = require('path');

function copyFileSync( source, target ) {

    var targetFile = target;

    //if target is a directory a new file with the same name will be created
    if ( fs.existsSync( target ) ) {
        if ( fs.lstatSync( target ).isDirectory() ) {
            targetFile = path.join( target, path.basename( source ) );
        }
    }

    fs.writeFileSync(targetFile, fs.readFileSync(source));
}

function copyFolderRecursiveSync( source, target ) {
    var files = [];

    //check if folder needs to be created or integrated
    var targetFolder = path.join( target, path.basename( source ) );
    if ( !fs.existsSync( targetFolder ) ) {
        fs.mkdirSync( targetFolder );
    }

    //copy
    if ( fs.lstatSync( source ).isDirectory() ) {
        files = fs.readdirSync( source );
        files.forEach( function ( file ) {
            var curSource = path.join( source, file );
            if ( fs.lstatSync( curSource ).isDirectory() ) {
                copyFolderRecursiveSync( curSource, targetFolder );
            } else {
                copyFileSync( curSource, targetFolder );
            }
        } );
    }
}

il ne copie pas les dossiers s'ils ont de l'espace dans leurs noms
31415926

Pour moi, il copie les dossiers avec des espaces dans leurs noms. Peut-être que cela a été causé par l'erreur corrigée par @victor. Comme j'utilise cette fonction assez régulièrement (dans l'état actuel, car j'ai oublié de mettre à jour la même correction que victor), je suis tout à fait sûr que cela fonctionne en général.
Simon Zyx

1
A également besoin de: javascript var fs = require('fs'); var path = require('path');
Tyler

2
Cela ne copie pas réellement les fichiers. Il les lit puis les écrit. Ce n'est pas de la copie. La copie comprend la date de création ainsi que d'autres flux de métadonnées pris en charge par Windows et MacOS et qui ne sont pas copiés par ce code. À partir du nœud 8.5, vous devez appeler fs.copyou fs.copySynccomme ils appellent réellement les fonctions de copie au niveau du système d'exploitation sous MacOS et Windows et ainsi copier les fichiers.
gman

1
désolé c'est fs.copyFileet si vous creusez dans la source du nœud que vous verrez sur Mac et Windows, ils appellent la fonction spécifique au système d'exploitation pour copier un fichier
gman

52

Certains modules prennent en charge la copie de dossiers avec leur contenu. Le plus populaire serait la clé

// Deep-copy an existing directory
wrench.copyDirSyncRecursive('directory_to_copy', 'location_where_copy_should_end_up');

Une alternative serait node-fs-extra

fs.copy('/tmp/mydir', '/tmp/mynewdir', function (err) {
  if (err) {
    console.error(err);
  } else {
    console.log("success!");
  }
}); //copies directory, even if it has subdirectories or files

3
la clé échoue si le répertoire à copier contient un lien symbolique
DoubleMalt

2
il échoue également sous Windows si le répertoire existe déjà, ncp a fonctionné dès la sortie du sac.
blented le

6
node-fs-extra a fonctionné pour moi. Il hérite du fs original et j'ai aimé sa façon de gérer le processus. Moins de code à mettre à jour dans l'application.
dvdmn

15
Veuillez noter que cela wrenchest obsolète et devrait être remplacé par node-fs-extra( github.com/jprichardson/node-fs-extra )
Ambidex

1
Wrench ne copie pas réellement les fichiers. Il les lit puis les écrit, puis copie leur date. Ce n'est pas de la copie. La copie comprend d'autres flux de métadonnées pris en charge par Windows et MacOS et qui ne sont pas copiés par clé.
gman

38

Voici une fonction qui copie récursivement un répertoire et son contenu dans un autre répertoire:

const fs = require("fs")
const path = require("path")

/**
 * Look ma, it's cp -R.
 * @param {string} src The path to the thing to copy.
 * @param {string} dest The path to the new copy.
 */
var copyRecursiveSync = function(src, dest) {
  var exists = fs.existsSync(src);
  var stats = exists && fs.statSync(src);
  var isDirectory = exists && stats.isDirectory();
  if (isDirectory) {
    fs.mkdirSync(dest);
    fs.readdirSync(src).forEach(function(childItemName) {
      copyRecursiveSync(path.join(src, childItemName),
                        path.join(dest, childItemName));
    });
  } else {
    fs.copyFileSync(src, dest);
  }
};

3
Même si vous insérez une fonction de copie réelle, vous ne devriez pas suivre les liens symboliques (utiliser à la fs.lstatSyncplace de fs.statSync)
Simon Zyx

3
ce qui pourrait avoir causé cette confusion est que fs.unlink supprime les fichiers, mais fs.link ne copie pas mais lie.
Simon Zyx

3
@SimonSeyock: a raison .. IL linkingne copie pas .. Le problème est que lorsque vous modifiez le contenu du fichier lié, le fichier d'origine changera également.
Abdennour TOUMI


23

Pour les OS Linux / Unix, vous pouvez utiliser la syntaxe du shell

const shell = require('child_process').execSync ; 

const src= `/path/src`;
const dist= `/path/dist`;

shell(`mkdir -p ${dist}`);
shell(`cp -r ${src}/* ${dist}`);

C'est tout!


1
Vous êtes le bienvenu 👋
Abdennour TOUMI

1
C'est la solution la plus simple. Pas besoin de réinventer les outils UNIX!
Michael Franzl

11
puisque nodejs fonctionne sous OSX / linux / windows, ce n'est que la réponse pour 2 pas tous 3.
mjwrazor

2
@AbdennourTOUMI et si vous exécutez sur un serveur Windows.
mjwrazor

3
C'est pourquoi j'ai commencé la réponse par "Pour les OS Linux / Unix, vous pouvez utiliser la syntaxe du shell .." 👍🏼
Abdennour TOUMI

19

Le module fs-extra fonctionne comme un charme.

Installez fs-extra

$ npm install fs-extra

Voici le programme pour copier le répertoire source dans le répertoire de destination.

// include fs-extra package
var fs = require("fs-extra");

var source = 'folderA'
var destination = 'folderB'

// copy source folder to destination
fs.copy(source, destination, function (err) {
    if (err){
        console.log('An error occured while copying the folder.')
        return console.error(err)
    }
    console.log('Copy completed!')
});

Références

fs-extra: https://www.npmjs.com/package/fs-extra

Exemple: Tutoriel NodeJS - Node.js Copier un dossier


ce processus remplace-t-il le répertoire ou fusionne-t-il avec lui?
SM Shahinul Islam

14

Voici comment je le ferais personnellement:

function copyFolderSync(from, to) {
    fs.mkdirSync(to);
    fs.readdirSync(from).forEach(element => {
        if (fs.lstatSync(path.join(from, element)).isFile()) {
            fs.copyFileSync(path.join(from, element), path.join(to, element));
        } else {
            copyFolderSync(path.join(from, element), path.join(to, element));
        }
    });
}

fonctionne pour les dossiers et les fichiers


3
Cette solution est concise et simple. Ce serait presque exactement comment je le ferais, donc un +1 de ma part. Vous devriez améliorer votre réponse avec des commentaires dans votre code et décrire pourquoi cette solution est préférée aux autres et quels sont ses inconvénients. - Mettez également à jour les modules dont il a besoin. ("path", "fs")
Andrew

vérifier si le dossier existe en haut ... sauvera des vies ;-) if (! fs.existsSync (to)) fs.mkdirSync (to);
Tobias

9

J'ai créé un petit exemple de travail qui copie un dossier source dans un autre dossier de destination en quelques étapes seulement (basé sur la réponse @ shift66 utilisant ncp):

étape 1 - Installez le module ncp:

npm install ncp --save

étape 2 - créez copy.js (modifiez les variables srcPath et destPath en fonction de vos besoins):

var path = require('path');
var ncp = require('ncp').ncp;

ncp.limit = 16;

var srcPath = path.dirname(require.main.filename); //current folder
var destPath = '/path/to/destination/folder'; //Any destination folder

console.log('Copying files...');
ncp(srcPath, destPath, function (err) {
  if (err) {
    return console.error(err);
  }
  console.log('Copying files complete.');
});

étape 3 - exécuter

node copy.js

7

C'est assez facile avec le nœud 10.

const FSP = require('fs').promises;

async function copyDir(src,dest) {
    const entries = await FSP.readdir(src,{withFileTypes:true});
    await FSP.mkdir(dest);
    for(let entry of entries) {
        const srcPath = Path.join(src,entry.name);
        const destPath = Path.join(dest,entry.name);
        if(entry.isDirectory()) {
            await copyDir(srcPath,destPath);
        } else {
            await FSP.copyFile(srcPath,destPath);
        }
    }
}

Cela suppose qu'il destn'existe pas.


3
Nous pouvons faire en sorte que cela fonctionne dans Node 8.x en utilisant require('util').promisifyavec fs.mkdiret fs.copyFileau lieu de require('fs').promises, qui est encore expérimental à la v11.1.
Sơn Trần-Nguyễn

@sntran 8.x a-t-il l' withFileTypesoption? Parce que cela vous évite un statappel
mpen

Malheureusement, 8.x n'a pas d' withFileTypesoption.
Sơn Trần-Nguyễn

@ SơnTrần-Nguyễn 8.x arrive en fin de vie le 31 décembre 2019 - il est peut-être temps de mettre à niveau :-)
mpen

6

Je connais déjà tellement de réponses ici mais personne n'y a répondu de manière simple. Concernant la documentation officielle de fs-exra , vous pouvez le faire très facilement

const fs = require('fs-extra')

// copy file
fs.copySync('/tmp/myfile', '/tmp/mynewfile')

// copy directory, even if it has subdirectories or files
fs.copySync('/tmp/mydir', '/tmp/mynewdir')

assurez-vous de définir l'option récursive. fs.copySync ('/ tmp / mydir', '/ tmp / mynewdir', {recursive: true})
Dheeraj Kumar

Je ne trouve pas l'option { recursive: true }de github doc que vous avez mentionnée, je ne sais pas si cela fonctionne.
Freddy Daniel du

Je suppose que nous parlons de fs-extra, mais votre lien github pointe vers node-fs-extra. Pourrait être une bibliothèque différente?
Dheeraj Kumar

@DheerajKumar, il montre node-fs-extra dans github mais fs-extra dans npm . Je ne sais pas que les deux sont identiques, veuillez vous référer au paquet de npm
Freddy Daniel

Est-ce que fs-extra remplace fs?
Matt

4

Étant donné que je ne fais que créer un simple script de nœud, je ne voulais pas que les utilisateurs du script aient besoin d'importer un tas de modules externes et de dépendances, alors j'ai mis mon cap de réflexion et j'ai fait une recherche pour exécuter des commandes à partir de la bash coquille.

Cet extrait de code node.js copie récursivement un dossier appelé node-webkit.app dans un dossier appelé build:

   child = exec("cp -r node-webkit.app build", function(error, stdout, stderr) {
        sys.print("stdout: " + stdout);
        sys.print("stderr: " + stderr);
        if(error !== null) {
            console.log("exec error: " + error);
        } else {

        }
    });

Merci à Lance Pollard de dzone pour m'avoir lancé.

L'extrait de code ci-dessus est limité aux plates-formes basées sur Unix telles que Mac OS et Linux, mais une technique similaire peut fonctionner pour Windows.


4

@ mallikarjun-m merci!

fs-extra a fait la chose et il peut même retourner Promise si vous ne fournissez pas de rappel! :)

const path = require('path')
const fs = require('fs-extra')

let source = path.resolve( __dirname, 'folderA')
let destination = path.resolve( __dirname, 'folderB')

fs.copy(source, destination)
  .then(() => console.log('Copy completed!'))
  .catch( err => {
    console.log('An error occured while copying the folder.')
    return console.error(err)
  })

2

Celui avec le support des liens symboliques + ne lance pas si le répertoire existe.

function copyFolderSync(from, to) {
  try {
    fs.mkdirSync(to);
  } catch(e) {}

  fs.readdirSync(from).forEach((element) => {
    const stat = fs.lstatSync(path.join(from, element));
    if (stat.isFile()) {
      fs.copyFileSync(path.join(from, element), path.join(to, element));
    } else if (stat.isSymbolicLink()) {
      fs.symlinkSync(fs.readlinkSync(path.join(from, element)), path.join(to, element));
    } else if (stat.isDirectory()) {
      copyFolderSync(path.join(from, element), path.join(to, element));
    }
  });
}

1

Ce code fonctionnera très bien, copiant récursivement n'importe quel dossier vers n'importe quel emplacement. Windows seulement.

var child=require("child_process");
function copySync(from,to){
    from=from.replace(/\//gim,"\\");
    to=to.replace(/\//gim,"\\");
    child.exec("xcopy /y /q \""+from+"\\*\" \""+to+"\\\"");
}

Fonctionne parfaitement pour mon jeu textuel pour créer de nouveaux joueurs.


1

J'ai essayé fs-extra et copy-dir pour copier-dossier-récursivement. mais je le veux

  1. fonctionne normalement (copy-dir génère une erreur irrésonnable)
  2. fournit deux arguments dans le filtre: filepath et filetype (fs-extra ne dit pas le type de fichier)
  3. a une vérification de répertoire à sous-répertoire et une vérification de répertoire à fichier

Alors j'ai écrit le mien:

//node module for node 8.6+
var path=require("path");
var fs=require("fs");

function copyDirSync(src,dest,options){
  var srcPath=path.resolve(src);
  var destPath=path.resolve(dest);
  if(path.relative(srcPath,destPath).charAt(0)!=".")
    throw new Error("dest path must be out of src path");
  var settings=Object.assign(Object.create(copyDirSync.options),options);
  copyDirSync0(srcPath,destPath,settings);
  function copyDirSync0(srcPath,destPath,settings){
    var files=fs.readdirSync(srcPath);
    if (!fs.existsSync(destPath)) {
      fs.mkdirSync(destPath);
    }else if(!fs.lstatSync(destPath).isDirectory()){
      if(settings.overwrite)
        throw new Error(`Cannot overwrite non-directory '${destPath}' with directory '${srcPath}'.`);
      return;
    }
    files.forEach(function(filename){
      var childSrcPath=path.join(srcPath,filename);
      var childDestPath=path.join(destPath,filename);
      var type=fs.lstatSync(childSrcPath).isDirectory()?"directory":"file";
      if(!settings.filter(childSrcPath,type))
        return;
      if (type=="directory") {
        copyDirSync0(childSrcPath,childDestPath,settings);
      } else {
        fs.copyFileSync(childSrcPath, childDestPath, settings.overwrite?0:fs.constants.COPYFILE_EXCL);
        if(!settings.preserveFileDate)
          fs.futimesSync(childDestPath,Date.now(),Date.now());
      }
    });
  }
}
copyDirSync.options={
  overwrite: true,
  preserveFileDate: true,
  filter: function(filepath,type){return true;}
};

et une fonction similaire mkdirs, qui est une alternative à mkdirp

function mkdirsSync(dest) {
  var destPath=path.resolve(dest);
  mkdirsSync0(destPath);
  function mkdirsSync0(destPath){
    var parentPath=path.dirname(destPath);
    if(parentPath==destPath)
      throw new Error(`cannot mkdir ${destPath}, invalid root`);
    if (!fs.existsSync(destPath)) {
      mkdirsSync0(parentPath);
      fs.mkdirSync(destPath);
    }else if(!fs.lstatSync(destPath).isDirectory()){
      throw new Error(`cannot mkdir ${destPath}, a file already exists there`);
    }
  }
}

0

J'ai écrit cette fonction pour copier (copyFileSync) ou déplacer (renameSync) des fichiers de manière récursive entre les répertoires:

//copy files
copyDirectoryRecursiveSync(sourceDir, targetDir);
//move files
copyDirectoryRecursiveSync(sourceDir, targetDir, true);


function copyDirectoryRecursiveSync(source, target, move) {
if (!fs.lstatSync(source).isDirectory()) return;

var operation = move ? fs.renameSync : fs.copyFileSync;
fs.readdirSync(source).forEach(function (itemName) {
    var sourcePath = path.join(source, itemName);
    var targetPath = path.join(target, itemName);

    if (fs.lstatSync(sourcePath).isDirectory()) {
        fs.mkdirSync(targetPath);
        copyDirectoryRecursiveSync(sourcePath, targetDir);
    }
    else {
        operation(sourcePath, targetPath);
    }
});}

0

Si vous êtes sous Linux et que les performances ne sont pas un problème, vous pouvez utiliser la execfonction du child_processmodule, pour exécuter une commande bash:

const { exec } = require('child_process');
exec('cp -r source dest', (error, stdout, stderr) => {...});

Dans certains cas, j'ai trouvé cette solution plus propre que de télécharger un module entier ou même d'utiliser un fsmodule.


0

ncp verrouille le descripteur de fichier et déclenche un rappel lorsqu'il n'a pas encore été déverrouillé. Je recommande d'utiliser le module de copie récursive à la place. Il prend en charge les événements et vous pouvez être sûr de la fin de la copie.


0

Soyez prudent lorsque vous choisissez votre colis. Certains packages comme copy-dir ne prennent pas en charge la copie de gros fichiers de plus de 0x1fffffe8 caractères. Cela lancera une erreur comme:

buffer.js:630 Uncaught Error: Cannot create a string longer than 0x1fffffe8 characters 

J'ai vécu quelque chose comme ça dans l'un de mes projets. En fin de compte, j'ai dû changer le package que j'utilisais et ajuster beaucoup de code. Je dirais que ce n'est pas une expérience très agréable.

Si vous souhaitez une copie source et une copie de destination multiples, vous pouvez utiliser une meilleure copie et écrire quelque chose comme ceci:

// copy from multiple source into a directory
bCopy(['/path/to/your/folder1', '/path/to/some/file.txt'], '/path/to/destination/folder');

ou même :

// copy from multiple source into multiple destination
bCopy(['/path/to/your/folder1', '/path/to/some/file.txt'], ['/path/to/destination/folder', '/path/to/another/folder']);

-1

OUI, ncpc'estcool bien ...

Vous voudrez peut-être / devriez promettre sa fonction de faire est super cool. Puisque vous y êtes, ajoutez-le à untools fichier pour le réutiliser.

Voici une version de travail qui est Asyncet utilise Promises.


index.js

const {copyFolder} = require('./tools/');

return copyFolder(
    yourSourcePath,
    yourDestinationPath
)
.then(() => {
    console.log('-> Backup completed.')
}) .catch((err) => {
    console.log("-> [ERR] Could not copy the folder: ", err);
})

tools.js

const ncp = require("ncp");

/**
 * Promise Version of ncp.ncp()
 * 
 * This function promisifies ncp.ncp().
 * We take the asynchronous function ncp.ncp() with 
 * callback semantics and derive from it a new function with
 * promise semantics.
 */
ncp.ncpAsync = function (sourcePath, destinationPath) {
  return new Promise(function (resolve, reject) {
      try {
          ncp.ncp(sourcePath, destinationPath, function(err){
              if (err) reject(err); else resolve();
          });
      } catch (err) {
          reject(err);
      }
  });
};

/**
 * Utility function to copy folders asynchronously using
 * the Promise returned by ncp.ncp(). 
 */
const copyFolder = (sourcePath, destinationPath) => {
    return ncp.ncpAsync(sourcePath, destinationPath, function (err) {
        if (err) {
            return console.error(err);
        }
    });
}
module.exports.copyFolder = copyFolder;

-1

L'approche la plus simple pour ce problème consiste à n'utiliser que les modules «fs» et «Path» et un peu de logique .....

Tous les fichiers dans le dossier racine sont copiés avec le nouveau nom si vous voulez simplement définir le numéro de version, c'est-à-dire ....................... "var v = 'Votre répertoire Nom'"

dans le contenu du préfixe de nom de fichier V ajouté avec le nom de fichier.

var fs = require('fs-extra');
var path = require('path');

var c = 0;
var i =0 ;
var v = "1.0.2";
var copyCounter = 0;
var directoryCounter = 0; 
var directoryMakerCounter = 0;
var recursionCounter = -1;
var Flag = false;
var directoryPath = [] ;
var directoryName = [] ;
var directoryFileName = [];
var fileName;
var directoryNameStorer;
var dc = 0;
var route ;



if (!fs.existsSync(v)){
   fs.mkdirSync(v);
}

var basePath = path.join(__dirname, v);


function walk(dir){

  fs.readdir(dir, function(err, items) {

    items.forEach(function(file){

        file = path.resolve(dir, file);

        fs.stat(file, function(err, stat){
            if(stat && stat.isDirectory()){

                directoryNameStorer = path.basename(file);
                route = file;
                route = route.replace("gd",v);

                directoryFileName[directoryCounter] = route;
                directoryPath[directoryCounter] = file;
                directoryName[directoryCounter] = directoryNameStorer;

                directoryCounter++;
                dc++;

                if (!fs.existsSync(basePath+"/"+directoryName[directoryMakerCounter])){
                    fs.mkdirSync(directoryFileName[directoryMakerCounter]);
                    directoryMakerCounter++;
                }

            }else{

                    fileName = path.basename(file);
                    if(recursionCounter >= 0){
                        fs.copyFileSync(file, directoryFileName[recursionCounter]+"/"+v+"_"+fileName, err => {
                            if(err) return console.error(err);
                        });
                        copyCounter++;
                    }else{
                        fs.copyFileSync(file, v+"/"+v+"_"+fileName, err => {
                            if(err) return console.error(err);
                        });
                        copyCounter++;    
                    }

                }
                if(copyCounter + dc == items.length && directoryCounter > 0 && recursionCounter < directoryMakerCounter-1){
                    console.log("COPY COUNTER :             "+copyCounter);
                    console.log("DC COUNTER :               "+dc);                        
                    recursionCounter++;
                    dc = 0;
                    copyCounter = 0;
                    console.log("ITEM DOT LENGTH :          "+items.length);
                    console.log("RECURSION COUNTER :        "+recursionCounter);
                    console.log("DIRECOTRY MAKER COUNTER :  "+directoryMakerCounter);
                    console.log(": START RECURSION :        "+directoryPath[recursionCounter]);
                    walk(directoryPath[recursionCounter]); //recursive call to copy sub-folder

                }

        })
    })
 });

}
 walk('./gd', function(err, data){ //Just Pass The Root Directory Which You Want to Copy
 if(err) throw err;
 console.log("done");
})

-1

Voici comment j'ai fait:

let fs = require('fs');
let path = require('path');

puis:

let filePath = //your FilePath

let fileList = []
        var walkSync = function(filePath, filelist) 
        {
          let files = fs.readdirSync(filePath);
          filelist = filelist || [];
          files.forEach(function(file) 
          {
            if (fs.statSync(path.join(filePath, file)).isDirectory()) 
            {
              filelist = walkSync(path.join(filePath, file), filelist);
            }
            else 
            {
              filelist.push(path.join(filePath, file));
            }
          });

          // Ignore hidden files
          filelist = filelist.filter(item => !(/(^|\/)\.[^\/\.]/g).test(item));

          return filelist;
        };

Puis appelez la méthode:

This.walkSync(filePath, fileList)
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.