是否有一种更简单的方法来复制文件夹及其所有内容,而无需手动执行一系列的fs。readir, fs。readfile, fs。writefile递归?
我只是想知道我是否错过了一个函数,理想情况下是这样工作的:
fs.copy("/path/to/source/folder", "/path/to/destination/folder");
关于这个历史问题。注意fs。Cp和fs。cpSync可以递归复制文件夹,在Node v16+中可用
是否有一种更简单的方法来复制文件夹及其所有内容,而无需手动执行一系列的fs。readir, fs。readfile, fs。writefile递归?
我只是想知道我是否错过了一个函数,理想情况下是这样工作的:
fs.copy("/path/to/source/folder", "/path/to/destination/folder");
关于这个历史问题。注意fs。Cp和fs。cpSync可以递归复制文件夹,在Node v16+中可用
当前回答
这是我解决这个问题的方法,没有任何额外的模块。只使用内置的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 );
}
} );
}
}
其他回答
有一些模块支持复制文件夹及其内容。最流行的是wrench.js:
// 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
打印稿版本
async function copyDir(source: string, destination: string): Promise<any> {
const directoryEntries = await readdir(source, { withFileTypes: true });
await mkdir(destination, { recursive: true });
return Promise.all(
directoryEntries.map(async (entry) => {
const sourcePath = path.join(source, entry.name);
const destinationPath = path.join(destination, entry.name);
return entry.isDirectory()
? copyDir(sourcePath, destinationPath)
: copyFile(sourcePath, destinationPath);
})
);
}
我尝试了fs-extra和copy-dir来递归地复制文件夹。但我希望它能
正常工作(copy-dir抛出一个不合理的错误) 在过滤器中提供两个参数:filepath和filetype (fs-extra不告诉文件类型) 有从目录到子目录的检查和从目录到文件的检查吗
所以我自己写了:
// Node.js module for Node.js 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`);
}
}
}
由于我只是构建一个简单的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。
这可能是一个可能的解决方案使用异步生成器函数和迭代等待循环。这个解决方案包括过滤掉一些目录的可能性,将它们作为可选的第三个数组参数传递。
import path from 'path';
import { readdir, copy } from 'fs-extra';
async function* getFilesRecursive(srcDir: string, excludedDir?: PathLike[]): AsyncGenerator<string> {
const directoryEntries: Dirent[] = await readdir(srcDir, { withFileTypes: true });
if (!directoryEntries.length) yield srcDir; // If the directory is empty, return the directory path.
for (const entry of directoryEntries) {
const fileName = entry.name;
const sourcePath = resolvePath(`${srcDir}/${fileName}`);
if (entry.isDirectory()) {
if (!excludedDir?.includes(sourcePath)) {
yield* getFilesRecursive(sourcePath, excludedDir);
}
} else {
yield sourcePath;
}
}
}
然后:
for await (const filePath of getFilesRecursive(path, ['dir1', 'dir2'])) {
await copy(filePath, filePath.replace(path, path2));
}