Copia la cartella in modo ricorsivo in node.js


154

C'è un modo più semplice per copiare una cartella e tutto il suo contenuto senza fare manualmente una sequenza di fs.readir, fs.readfile, fs.writefilein modo ricorsivo?

Mi chiedo solo se mi manca una funzione che idealmente funzionerebbe così

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

3
C'è un modo per farlo senza moduli? Forse una funzione / codice ricorsivo snip-it?
Sukima,

@Sukima - Vedi la mia risposta qui .
jmort253,

Risposte:


121

Puoi usare il modulo ncp . Penso che questo sia ciò di cui hai bisogno


2
Perfetto! npm install ncpe lavorare in meno di 30 anni. Grazie.
Aebsubis,

1
Wrench è meglio per me, perché supporta più opzioni. Con NCP, ad esempio, non è possibile risolvere collegamenti simbolici.
Slava Fomin II,

3
Come bonus straordinario, si può usare ncp in uno script di esecuzione npm multipiattaforma.
Ciantic,

Ho avuto alcuni semplici casi in cui ncp non rientra nel mio callback dove fs-extra funziona correttamente.
bumpmann,

40
Si noti che ncp sembra non essere mantenuto . fs-extra è probabilmente l'opzione migliore invece.
chris,

74

Questo è il mio approccio per risolvere questo problema senza moduli aggiuntivi. Basta usare il modulo integrato fse i pathmoduli.

Nota: questo utilizza le funzioni di lettura / scrittura di fs in modo da non copiare alcun metadati (tempo di creazione ecc.). A partire dal nodo 8.5 sono copyFileSyncdisponibili funzioni che richiamano le funzioni di copia del sistema operativo e quindi copiano anche i metadati. Non li ho ancora testati, ma dovrebbe funzionare solo per sostituirli. (Vedi 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 );
            }
        } );
    }
}

non copia le cartelle se hanno spazio nei loro nomi
31415926

Per me copia le cartelle con spazi nei loro nomi. Forse è stato causato dall'errore corretto da @victor. Poiché sto usando questa funzione abbastanza regolarmente (nello stato attuale, poiché ho dimenticato di aggiornare lo stesso vincitore della correzione), sono abbastanza sicuro che funzioni in generale.
Simon Zyx,

1
Ha anche bisogno di: javascript var fs = require('fs'); var path = require('path');
Tyler,

2
Questo in realtà non copia i file. Li legge e poi li scrive. Non è una copia. La copia include la data di creazione e altri flussi di metadati supportati da Windows e MacOS e non copiati da questo codice. A partire dal nodo 8.5 dovresti chiamare fs.copyo fs.copySynccome chiamano effettivamente le funzioni di copia a livello di sistema operativo in MacOS e Windows e quindi effettivamente copiare i file.
Gman,

1
mi dispiace fs.copyFilee se scavi attraverso il nodo sorgente vedrai su Mac e Windows che chiamano la funzione specifica del sistema operativo per copiare un file
gman

52

Esistono alcuni moduli che supportano la copia di cartelle con il loro contenuto. Il più popolare sarebbe chiave inglese

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

Un'alternativa sarebbe 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 chiave inglese non riesce se la directory da copiare contiene un collegamento simbolico
DoubleMalt

2
fallisce anche su Windows se la directory esiste già, ncp ha funzionato immediatamente.
blessed

6
node-fs-extra ha funzionato per me. Eredita la fs originale e mi è piaciuto il suo modo di gestire il processo. Meno codice da aggiornare nell'app.
dvdmn,

15
Si noti che wrenchè stato deprecato e deve essere sostituito da node-fs-extra( github.com/jprichardson/node-fs-extra )
Ambidex

1
Wrench in realtà non copia i file. Li legge, poi li scrive, quindi copia la loro data. Non è una copia. La copia include altri flussi di metadati supportati da Windows e MacOS e non copiati dalla chiave inglese.
Gman,

38

Ecco una funzione che copia ricorsivamente una directory e il suo contenuto in un'altra directory:

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
Anche se si inserisce una funzione di copia reale non è necessario seguire i collegamenti simbolici (utilizzare fs.lstatSyncinvece di fs.statSync)
Simon Zyx,

3
ciò che potrebbe aver causato questa confusione è che fs.unlink elimina i file, ma fs.link non copia ma collega.
Simon Zyx,

3
@SimonSeyock: ha ragione .. IT linkingnon sta copiando .. Il problema è quando si modifica il contenuto del file collegato, anche il file originale cambierà.
Abdennour TOUMI


22

Per il sistema operativo linux / unix, è possibile utilizzare la sintassi della shell

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

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

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

Questo è tutto!


1
Sei il benvenuto 👋
Abdennour TOUMI

1
Questa è la soluzione più semplice. Non è necessario reinventare gli strumenti UNIX!
Michael Franzl,

11
poiché nodejs funziona su OSX / linux / windows questa è solo la risposta per 2 non tutti 3.
mjwrazor

2
@AbdennourTOUMI cosa succede se si esegue su Windows Server.
mjwrazor,

3
Ecco perché ho iniziato la risposta di "Per Linux / unix OS, è possibile utilizzare la sintassi della shell .." Ab
Abdennour TOUMI

19

Il modulo fs-extra funziona come un incantesimo.

Installa fs-extra

$ npm install fs-extra

Di seguito è riportato il programma per copiare la directory di origine nella directory di destinazione.

// 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!')
});

Riferimenti

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

Esempio: NodeJS Tutorial - Node.js Copia una cartella


questo processo sostituisce la directory o si fonde con essa?
SM Shahinul Islam,

14

Ecco come lo farei personalmente:

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));
        }
    });
}

funziona per cartelle e file


3
Questa soluzione è concisa e semplice. Questo sarebbe quasi esattamente come lo farei, quindi un +1 da parte mia. Dovresti migliorare la tua risposta con commenti nel tuo codice e descrivere perché questa soluzione è preferita rispetto ad altre e quali svantaggi potrebbe avere. - Aggiorna anche i moduli richiesti. ("path", "fs")
Andrew,

controlla se la cartella esiste in alto ... salverà delle vite ;-) if (! fs.existsSync (to)) fs.mkdirSync (to);
Tobias

9

Ho creato un piccolo esempio funzionante che copia una cartella di origine in un'altra cartella di destinazione in pochi passaggi (basato sulla risposta @ shift66 usando ncp):

passaggio 1: installare il modulo ncp:

npm install ncp --save

passaggio 2: crea copy.js (modifica i parametri srcPath e destPath in base alle tue esigenze):

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.');
});

passaggio 3 - eseguire

node copy.js

7

Questo è abbastanza semplice con il nodo 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);
        }
    }
}

Questo presuppone destche non esista.


3
Possiamo farlo funzionare nel Nodo 8.x usando require('util').promisifycon fs.mkdire fs.copyFileinvece di require('fs').promises, che è ancora sperimentale su v11.1.
Sơn Trần-Nguyễn,

@sntran 8.x ha l' withFileTypesopzione? Perché questo ti fa risparmiare una statchiamata
mpen

Sfortunatamente, 8.x non ha withFileTypesopzione.
Sơn Trần-Nguyễn,

@ SơnTrần-Nguyễn 8.x raggiunge la fine della vita il 31 dicembre 2019 - potrebbe essere il momento di aggiornare :-)
mpen

6

Conosco così tante risposte già qui ma nessuno ha risposto in modo semplice. Per quanto riguarda la documentazione ufficiale di fs-exra , puoi farlo molto facilmente

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')

assicurati di impostare l'opzione ricorsiva. fs.copySync ('/ tmp / mydir', '/ tmp / mynewdir', {ricorsivo: vero})
Dheeraj Kumar,

Non riesco a trovare un'opzione { recursive: true }dal documento github che hai citato, non so se funziona.
Freddy Daniel,

Immagino che stiamo parlando di fs-extra, ma il tuo link github punta a node-fs-extra. Potrebbe essere una libreria diversa?
Dheeraj Kumar,

@DheerajKumar, mostra node-fs-extra in github ma fs-extra in npm . Non so che entrambi siano uguali, si prega di fare riferimento al pacchetto di npm
Freddy Daniel,

Fs-extra sostituisce fs?
Matt,

4

Dato che sto solo costruendo un semplice script di nodo, non volevo che gli utenti dello script avessero bisogno di importare un sacco di moduli esterni e dipendenze, quindi mi sono messo il pensiero pensante e ho fatto una ricerca per eseguire comandi dalla bash conchiglia.

Questo frammento di codice node.js copia ricorsivamente una cartella chiamata node-webkit.app in una cartella chiamata 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 {

        }
    });

Grazie a Lance Pollard a dzone per avermi avviato.

Lo snippet sopra è limitato alle piattaforme basate su Unix come Mac OS e Linux, ma una tecnica simile potrebbe funzionare per Windows.


4

@ mallikarjun-m grazie!

fs-extra ha fatto la cosa e può anche restituire Promise se non fornisci callback! :)

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

Quello con supporto simbolico link + non viene lanciato se esiste una directory.

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

Questo codice funzionerà perfettamente, copiando ricorsivamente qualsiasi cartella in qualsiasi posizione. Solo Windows.

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

Funziona perfettamente con il mio gioco basato sul testo per la creazione di nuovi giocatori.


1

Ho provato fs-extra e copy-dir a copiare-cartella-ricorsivamente. ma lo voglio

  1. funziona normalmente (copy-dir genera un errore irreversibile)
  2. fornisce due argomenti nel filtro: filepath e filetype (fs-extra non dice il tipo di file)
  3. ha il controllo dir-to-subdir e il controllo dir-to-file

Quindi ho scritto il mio:

//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;}
};

e una funzione simile mkdirs, che è un'alternativa a 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

Ho scritto questa funzione sia per copiare (copyFileSync) che per spostare (renameSync) file in modo ricorsivo tra le directory:

//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

Se sei in Linux e le prestazioni non sono un problema, puoi usare la execfunzione from child_processmodule, per eseguire un comando bash:

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

In alcuni casi, ho trovato questa soluzione più pulita rispetto al download di un intero modulo o persino all'utilizzo del fsmodulo.


0

ncp blocca il descrittore di file e attiva la richiamata quando non è stata ancora sbloccata. Consiglio invece di utilizzare il modulo di copia ricorsiva . Supporta eventi e puoi essere sicuro del termine della copia.


0

Fai attenzione quando scegli il tuo pacchetto. Alcuni pacchetti come copy-dir non supportano la copia di file di grandi dimensioni di lunghezza superiore a 0x1fffffe8. Verrà generato un errore come:

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

Ho sperimentato qualcosa del genere in uno dei miei progetti. Alla fine, ho dovuto cambiare il pacchetto che stavo usando e regolare molto codice. Direi che questa non è un'esperienza molto piacevole.

Se si desidera copiare più origini e più destinazioni, è possibile utilizzare una copia migliore e scrivere qualcosa del genere:

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

o anche :

// 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

SÌ, lo ncpècool però ...

Potresti volere / promettere di promuoverne la funzione super cool. Dato che ci sei, aggiungilo atools file per riutilizzarlo.

Di seguito è una versione funzionante che è Asynce utilizza 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'approccio più semplice per questo problema è usare solo il modulo 'fs' e 'Path' e qualche logica .....

Tutti i file nella cartella principale vengono copiati con il nuovo nome se si desidera semplicemente impostare il numero di versione, ad es. ....................... "var v = 'Directory Nome'"

nel prefisso del nome file V contenuto aggiunto con il nome file.

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

Ecco come ho fatto:

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

poi:

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;
        };

Quindi chiama il metodo:

This.walkSync(filePath, fileList)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.