是否有一种更简单的方法来复制文件夹及其所有内容,而无需手动执行一系列的fs。readir, fs。readfile, fs。writefile递归?

我只是想知道我是否错过了一个函数,理想情况下是这样工作的:

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

关于这个历史问题。注意fs。Cp和fs。cpSync可以递归复制文件夹,在Node v16+中可用


当前回答

由于我只是构建一个简单的Node.js脚本,我不希望脚本的用户需要导入一堆外部模块和依赖项,所以我开始思考,并从Bash shell中搜索运行命令。

这个Node.js代码片段递归地复制了一个名为node-webkit的文件夹。应用程序到一个名为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的兰斯·波拉德让我开始。

上面的代码片段仅限于基于unix的平台,如macOS和Linux,但类似的技术也适用于Windows。

其他回答

挑选包裹时要小心。有些包(如copy-dir)不支持复制长度超过0X1FFFFFE8个字符(约537 MB)的大文件。

它会抛出一些错误,比如:

buffer.js:630 Uncaught Error:不能创建大于0x1fffffe8字符的字符串

在我的一个项目中,我就经历过类似的事情。最终,我不得不改变我正在使用的包并调整大量代码。我想说,这不是一个很愉快的经历。

如果需要多个源副本和多个目标副本,您可以使用better-copy并编写如下内容:

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

这在Node.js 10中非常简单:

const Path = require('path');
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不存在。

这是我个人的做法:

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

它适用于文件夹和文件。

这是我解决这个问题的方法,没有任何额外的模块。只使用内置的fs和path模块。

注意:这使用fs的读/写函数,所以它不复制任何元数据(创建时间等)。从Node.js 8.5开始,有一个copyFileSync函数可用,它调用操作系统复制函数,因此也复制元数据。我还没有测试它们,但它应该可以替换它们。(见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 );
            }
        } );
    }
}

从Node v16.7.0开始:

import { cp } from 'fs/promises';
await cp(
  new URL('../path/to/src/', import.meta.url),
  new URL('../path/to/dest/', import.meta.url), {
    recursive: true,
  }
);

注意使用递归:true。这可以防止ERR_FS_EISDIR错误。

阅读更多关于节点文件系统的文档