在node.js中递归复制文件夹


154

是否有一个文件夹及其内容全部复制,而无需手动做的序列更简单的方法fs.readirfs.readfilefs.writefile递归?

只是想知道我是否缺少理想地像这样工作的功能

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

3
没有任何模块,有没有办法做到这一点?也许是递归函数/代码片段吗?
Sukima

@Sukima- 在这里查看我的答案。
jmort253

Answers:


121

您可以使用ncp模块。我觉得这就是你所需要的


2
完善!npm install ncp并在30秒内完成工作。谢谢。
Aebsubis 2014年

1
扳手对我来说更好,因为它支持更多选择。例如,使用NCP不能解析符号链接。
Slava Fomin II

3
令人惊讶的是,可以在跨平台的npm运行脚本中使用ncp。
Ciantic'3

我有一些简单的情况,其中ncp不在fs-extra正确执行的回调中。
bumpmann

40
请注意,ncp 似乎未维护fs-extra可能是最好的选择。
克里斯,

74

这是我无需任何额外模块即可解决此问题的方法。仅使用内置fspath模块。

注意:这确实使用了fs的读/写功能,因此它不会复制任何元数据(创建时间等)。从节点8.5开始,有copyFileSync可用的函数调用OS复制功能,因此也复制元数据。我还没有测试它们,但是应该可以替换它们。(请参阅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 );
            }
        } );
    }
}

如果文件夹名称中有空格,则不会复制文件夹
31415926 2014年

对我来说,它确实复制名称中带有空格的文件夹。也许是由@victor纠正的错误引起的。由于我经常使用此功能(在当前状态下,因为我忘记更新相同的校正胜利者所做的事情),因此我可以肯定它确实可以正常工作。
Simon Zyx 2014年

1
还需要: javascript var fs = require('fs'); var path = require('path');
泰勒(Tyler)

2
这实际上并不复制文件。它先读取它们,然后写入它们。那不是复制。复制包括创建日期以及Windows和MacOS支持的其他元数据流,并且此代码不会复制这些元数据流。从节点8.5开始,您应该调用fs.copyfs.copySync实际调用MacOS和Windows中的OS级复制功能,因此实际上是在复制文件。
gman

1
抱歉fs.copyFile,如果您在Mac和Windows上浏览节点源时,会调用操作系统特定的功能来复制文件
gman

52

有些模块支持复制文件夹及其内容。最受欢迎的将是扳手

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

另一种选择是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
如果要复制的目录包含符号链接,扳手将失败
DoubleMalt 2013年

2
如果目录已经存在,它在Windows上也将失败,因此ncp可以立即使用
2013年

6
node-fs-extra为我工作。它继承了原始的fs,我喜欢它处理过程的方式。在应用程序中更新的代码更少。
dvdmn 2014年

15
请注意,wrench已被弃用,应改为node-fs-extragithub.com/jprichardson/node-fs-extra
Ambidex

1
扳手实际上并不复制文件。它先读取它们,然后写入它们,然后复制它们的日期。那不是复制。复制包括Windows和MacOS支持的其他元数据流,并且不能通过扳手复制。
gman

38

这是一个递归复制目录及其内容到另一个目录的函数:

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
即使您插入真正的复制功能,也不应遵循符号链接(使用fs.lstatSync代替fs.statSync
Simon Zyx 2014年

3
造成这种混乱的原因是fs.unlink删除了文件,但是fs.link不会复制而是链接。
Simon Zyx

3
@SimonSeyock:是的..它 linking没有复制..问题是当您修改链接文件的内容时,原始文件也会更改。
Abdennour TOUMI 2016年


22

对于linux / unix操作系统,可以使用shell语法

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

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

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

而已!


1
不客气Ab
Abdennour TOUMI

1
这是最简单的解决方案。无需重新发明UNIX工具!
Michael Franzl

11
由于nodejs在OSX / linux / windows上运行,这仅是2的答案,而不是全部3的答案
。– mjwrazor

2
@AbdennourTOUMI如果您在Windows服务器上运行该怎么办。
mjwrazor

3
这就是为什么我开始由回答“对Linux / Unix操作系统,你可以使用shell语法。”👍🏼
Abdennour•图米

19

fs-extra模块的工作原理很像。

安装fs-extra

$ npm install fs-extra

以下是将源目录复制到目标目录的程序。

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

参考文献

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

例如:的NodeJS教程 - Node.js的复制文件夹


这个过程会替换目录还是与其合并?
SM Shahinul Islam

14

这就是我个人要做的:

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

适用于文件夹和文件


3
该解决方案简洁明了。这几乎就是我的做法,因此我给了+1。您应该通过在代码中添加注释来改善答案,并说明为什么此解决方案优于其他解决方案,以及它可能具有哪些缺点。-同时更新所需的模块。(“路径”,“ fs”)
安德鲁(Andrew

检查文件夹是否位于顶部...将挽救生命;-)如果(!fs.existsSync(to))fs.mkdirSync(to);
Tobias

9

我创建了一个小的工作示例,仅需几个步骤即可将源文件夹复制到另一个目标文件夹(基于使用ncp的@ shift66答案):

步骤1-安装ncp模块:

npm install ncp --save

第2步-创建copy.js(将srcPath和destPath vars修改为所需的值):

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

第3步-运行

node copy.js

7

对于节点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);
        }
    }
}

这假定dest不存在。


3
我们能得到这个在节点8.x中使用的工作require('util').promisifyfs.mkdirfs.copyFile代替require('fs').promises,这是在V11.1仍处于试验阶段。
儿子TRAN-阮

@sntran 8.x是否可以withFileTypes选择?因为这样可以节省您的stat通话时间
mpen

不幸的是,8.x没有withFileTypes选项。
儿子TRAN-阮

@SơnTrần-Nguyễn8.x将于2019年12月31日到期,也许是时候升级了:-)
mpen

6

我已经在这里知道了很多答案,但是没有人以简单的方式回答。关于fs-exra 官方文档,您可以轻松完成

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

确保设置递归选项。fs.copySync('/ tmp / mydir','/ tmp / mynewdir',{递归:true})
Dheeraj Kumar,

{ recursive: true }从您提到的github文档中找不到选项,不知道它是否有效。
弗雷迪·丹尼尔

我猜我们在谈论fs-extra,但是您的github链接指向node-fs-extra。可以是其他图书馆吗?
Dheeraj Kumar,

@DheerajKumar,它在github中显示了node-fs-extra,但是在npm中显示了 fs-extra 。我不知道这两个都是相同的,请从参阅包NPM
弗雷迪丹尼尔

fs-extra取代fs吗?
马特

4

因为我只是构建一个简单的节点脚本,所以我不希望脚本的用户需要导入大量外部模块和依赖项,所以我戴上了帽子,并从bash中搜索了运行命令贝壳。

此node.js代码片段以递归方式将一个名为node-webkit.app的文件夹复制到一个名为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 {

        }
    });

感谢dzone的Lance Pollard让我入门。

上面的代码段仅限于基于Unix的平台(例如Mac OS和Linux),但是类似的技术可能适用于Windows。


4

@ mallikarjun-m谢谢!

fs-extra做的事情,如果不提供回调,它甚至可以返回Promise!:)

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

如果目录存在,则带有符号链接支持 +的那个不会抛出。

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

此代码可以正常工作,将任何文件夹递归复制到任何位置。仅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+"\\\"");
}

非常适合我的基于文本的游戏,用于创建新玩家。


1

我尝试了fs-extra和copy-dir递归地复制文件夹。但我想要

  1. 正常工作(copy-dir引发不合理的错误)
  2. 在过滤器中提供了两个参数:文件路径和文件类型(fs-extra不告诉文件类型)
  3. 具有目录到子目录检查和目录到文件检查

所以我写了我自己的:

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

和类似的函数mkdirs,它是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

我为在目录之间递归地复制(copyFileSync)或移动(renameSync)文件编写了此函数:

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

如果您使用的是Linux,并且性能不是问题,则可以使用exec来自child_process模块的函数来执行bash命令:

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

在某些情况下,我发现此解决方案比下载整个模块甚至使用fs模块都更干净。



0

挑选包裹时要小心。某些文件(如copy-dir)不支持复制长度超过0x1fffffe8个字符的大文件。它将引发一些错误,例如:

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

在我的一个项目中,我经历了类似的事情。最终,我不得不更改所使用的软件包并调整大量代码。我会说这不是一个非常愉快的经历。

如果需要多个源和多个目标副本,则可以使用更好的副本并编写如下内容:

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

甚至 :

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

是的,ncp就是cool虽然...

您可能希望/应该承诺其功能为is super cool。既然您在使用它,请将其添加到tools文件中以重新使用它。

以下是Async和使用的工作版本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

解决此问题的最简单方法是仅使用“ fs”和“ Path”模块以及一些逻辑.....

如果需要,根文件夹中的所有文件都将使用“新名称”进行复制,只需设置版本号即可,即.......................“ var v ='您的目录名称'”

在文件名前缀V内容中添加文件名。

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

这是我做的:

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

然后:

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

然后调用方法:

This.walkSync(filePath, fileList)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.