Javascript 在Node.js中复制文件的最快方法
我在Node.js上工作的项目意味着大量的文件系统复制、读取、写入等操作Javascript 在Node.js中复制文件的最快方法,javascript,node.js,Javascript,Node.js,我在Node.js上工作的项目意味着大量的文件系统复制、读取、写入等操作 哪些方法最快?使用标准内置方式: 如果您必须支持Node.js的旧版本,那么在不支持fs.copyFile的版本中,您可以这样做: 相同的机制,但这增加了错误处理: function copyFile(source, target, cb) { var cbCalled = false; var rd = fs.createReadStream(source); rd.on("error", function
哪些方法最快?使用标准内置方式: 如果您必须支持Node.js的旧版本,那么在不支持fs.copyFile的版本中,您可以这样做:
相同的机制,但这增加了错误处理:
function copyFile(source, target, cb) {
var cbCalled = false;
var rd = fs.createReadStream(source);
rd.on("error", function(err) {
done(err);
});
var wr = fs.createWriteStream(target);
wr.on("error", function(err) {
done(err);
});
wr.on("close", function(ex) {
done();
});
rd.pipe(wr);
function done(err) {
if (!cbCalled) {
cb(err);
cbCalled = true;
}
}
}
通常,避免异步文件操作是很好的。以下是简短的无错误处理同步示例:
var fs = require('fs');
fs.writeFileSync(targetFile, fs.readFileSync(sourceFile));
Mike Schilling的错误处理解决方案带有错误事件处理程序的快捷方式
function copyFile(source, target, cb) {
var cbCalled = false;
var rd = fs.createReadStream(source);
rd.on("error", done);
var wr = fs.createWriteStream(target);
wr.on("error", done);
wr.on("close", function(ex) {
done();
});
rd.pipe(wr);
function done(err) {
if (!cbCalled) {
cb(err);
cbCalled = true;
}
}
}
由于某种原因,我无法使createReadStream/createWriteStream方法工作,但使用npm模块它立即工作。不过,我不确定性能的差异
npm安装-节省fs额外费用
写得快,使用方便,承诺和错误管理:
function copyFile(source, target) {
var rd = fs.createReadStream(source);
var wr = fs.createWriteStream(target);
return new Promise(function(resolve, reject) {
rd.on('error', reject);
wr.on('error', reject);
wr.on('finish', resolve);
rd.pipe(wr);
}).catch(function(error) {
rd.destroy();
wr.end();
throw error;
});
}
与异步/等待语法相同:
async function copyFile(source, target) {
var rd = fs.createReadStream(source);
var wr = fs.createWriteStream(target);
try {
return await new Promise(function(resolve, reject) {
rd.on('error', reject);
wr.on('error', reject);
wr.on('finish', resolve);
rd.pipe(wr);
});
} catch (error) {
rd.destroy();
wr.end();
throw error;
}
}
如果您不关心它是异步的,并且不复制GB大小的文件,并且不希望仅为单个函数添加另一个依赖项:
function copySync(src, dest) {
var data = fs.readFileSync(src);
fs.writeFileSync(dest, data);
}
,但在复制之前还要检查文件的可见性:
function copy(from, to) {
return new Promise(function (resolve, reject) {
fs.access(from, fs.F_OK, function (error) {
if (error) {
reject(error);
} else {
var inputStream = fs.createReadStream(from);
var outputStream = fs.createWriteStream(to);
function rejectCleanup(error) {
inputStream.destroy();
outputStream.end();
reject(error);
}
inputStream.on('error', rejectCleanup);
outputStream.on('error', rejectCleanup);
outputStream.on('finish', resolve);
inputStream.pipe(outputStream);
}
});
});
}
,但承诺:
const FileSystem = require('fs');
exports.copyFile = function copyFile(source, target) {
return new Promise((resolve,reject) => {
const rd = FileSystem.createReadStream(source);
rd.on('error', err => reject(err));
const wr = FileSystem.createWriteStream(target);
wr.on('error', err => reject(err));
wr.on('close', () => resolve());
rd.pipe(wr);
});
};
另一个答案的改进 特点: 如果dst文件夹不存在,它将自动创建它。另一个答案只会抛出错误。 它返回一个承诺,这使得它更容易在更大的项目中使用。 它允许您复制多个文件,当所有文件都被复制时,承诺就会实现。 用法:
var onePromise = copyFilePromise("src.txt", "dst.txt");
var anotherPromise = copyMultiFilePromise(new Array(new Array("src1.txt", "dst1.txt"), new Array("src2.txt", "dst2.txt")));
代码:
以前所有不检查源文件是否存在的解决方案都是危险的。。。比如说,
fs.stat(source, function(err,stat) { if (err) { reject(err) }
否则,如果源和目标被错误替换,则场景中存在风险,您的数据将永久丢失而不会发现任何错误。自Node.js 8.5.0以来,我们有了新的和方法 用法示例:
var fs = require('fs');
// File "destination.txt" will be created or overwritten by default.
fs.copyFile('source.txt', 'destination.txt', (err) => {
if (err)
throw err;
console.log('source.txt was copied to destination.txt');
});
使用Node.js的内置复制功能
它同时提供异步和同步版本:
const fs = require('fs');
// File "destination.txt" will be created or overwritten by default.
fs.copyFile('source.txt', 'destination.txt', (err) => {
if (err)
throw err;
console.log('source.txt was copied to destination.txt');
});
对于快速复制,应使用fs.constants.COPYFILE_FICLONE标志。它允许支持此功能的文件系统不实际复制文件内容。只创建了一个新的文件条目,但它指向源文件的克隆 什么都不做/少做是做事最快的方式 设fs=requirefs; fs.copyFile source.txt, destination.txt, fs.constants.COPYFILE_FICLONE, 错误=>{ 如果出错{ //TODO:句柄错误 console.logerror; } 成功; } ; 改用承诺: 设fs=requirefs; 设util=requireutil; 让copyFile=util.promisifyfs.copyFile; 复制文件 source.txt, destination.txt, fs.constants.COPYFILE\u FICLONE .catch=>console.logerror .then=>console.logsuccess; 这就是我个人使用Node.js复制文件并替换另一个文件的方法:您可以使用fs extra模块非常轻松地完成此操作:
const fse = require('fs-extra');
let srcDir = 'path/to/file';
let destDir = 'pat/to/destination/directory';
fse.moveSync(srcDir, destDir, function (err) {
// To move a file permanently from a directory
if (err) {
console.error(err);
} else {
console.log("success!");
}
});
或
您可能希望使用async/await,因为节点v10.0.0可以使用内置的fs API 例如:
const fs = require('fs')
const copyFile = async (src, dest) => {
await fs.promises.copyFile(src, dest)
}
注:
从节点v11.14.0、v10.17.0开始,API不再是实验性的
更多信息:
我编写了一个小实用程序来测试不同的方法: 用它运行
npx copy-speed-test --source someFile.zip --destination someNonExistentFolder
它使用child_process.exec执行本机复制,使用fs.copyFile执行复制文件,并使用createReadStream和各种不同的缓冲区大小。您可以通过在命令行上传递它们来更改缓冲区大小。运行npx copy speed test-h获取更多信息。请记住,在现实生活中,您需要检查createReadStream和createWriteStream是否存在错误,因此,您不会得到一行程序,尽管它仍然会一样快。这比通过require'child_process.exec执行原始cp test.log newLog.log快/慢多少,与完整的Node.js解决方案相反。不幸的是,在我的系统上,使用streams与child_进程相比速度非常慢。execFile'/bin/cp',['-无目标目录',source,target]。我使用了这种方法,但我得到的只是一个写时的空白文件。你知道为什么吗?fs.createReadStream'/init/xxx.json'.pipefs.createWriteStream'xxx.json';这是个好问题,虽然有趣的是,当其他类似格式的问题因为不符合SO标准而立即获得3到4张反对票时,它获得了25张赞成票,但javascript标签可能是由更友善的人所抓取的:大多数情况下,在多年的浏览器规范化之后,我们对整个文件业务都感到新鲜和兴奋佩奇是。其他答案实际上都没有复制文件。MacOS和Windows上的文件还有其他元数据,这些元数据仅通过复制字节就丢失了。本页任何其他答案未复制的数据示例,以及。即使在Unix上,其他答案也不会复制创建日期,这在复制文件时通常很重要
错误触发两个流上的错误。源和目标流。如果源文件不存在,如何处理错误?在这种情况下仍然会创建目标文件。我认为WriteStream中的错误只会将其取消管道。你必须打电话给rd,摧毁你自己。至少我是这样的。遗憾的是,除了源代码之外,没有太多文档。cb代表什么?我们应该传递什么作为第三个参数?@SaiyanGirl'cb'代表回调。您应该传入一个函数。一般来说,这样说是非常错误的,特别是因为它会导致人们对向其服务器发出的每个请求都重新读取文件。这可能会很昂贵。使用*Sync方法完全违背nodejs的哲学!我还认为,它们正逐渐被弃用。nodejs的全部思想是它是单线程和事件驱动的。@gillyb我能想到的使用它们的唯一原因是为了简单-如果你正在编写一个只使用一次的快速脚本,你可能不会为阻止进程而烦恼。我不知道它们被弃用了。在web服务器上,同步方法几乎总是一个糟糕的主意,但有时在node webkit之类的应用中是理想的,因为它只在文件复制时锁定窗口中的操作。抛出一个加载gif,可能还有一个加载条,在某些点更新,让同步方法阻止所有操作,直到复制完成。与其说它是一个最佳实践,不如说它是一个时间和地点的问题。当您与另一个同步操作交互时,或者您希望执行顺序操作时,同步方法是可以的,也就是说,您无论如何都会模拟同步。如果操作是连续的,只需避免回调地狱和/或承诺汤,并使用同步方法。一般来说,在服务器上使用它们时应谨慎,但在涉及CLI脚本的大多数情况下都可以。这是目前最好的选择在节点中使用同步代码会降低应用程序的性能。哦,请。。。问题是关于复制文件的最快方法。虽然最快总是主观的,但我不认为同步代码有什么用。实现最快还是执行最快?不同的优先级意味着这是一个有效的答案。fs-extra还具有异步方法,即fs.copyrc、dst、callback;,这些应该可以解决@mvillar的问题。如果没有更多的输入存在,网络共享中断,但写入仍然成功,会发生什么?将同时调用reject from read和resolve from write吗?如果两个读/写都失败,读期间坏磁盘扇区,写期间满磁盘,该怎么办?然后将调用拒绝两次。不幸的是,基于Mike答案并带有标志的Promise解决方案似乎是正确考虑错误处理的唯一可行解决方案。一旦复制成功,Promise就会得到解决。如果它被拒绝,它的状态将被解决,多次调用reject也不会有什么区别。我刚刚测试了新Promisefunctionresolve,reject{resolve1;resolve2;reject3;reject4;console.logDONE;}.thenconsole.log.bindconsole,functione{console.logE,e;};在这一点上,你是对的:试图解决或拒绝已解决的承诺没有效果。也许您可以扩展您的答案,并解释您为什么以这种方式编写函数?谢谢:-顺便说一句,可写流的关闭应该结束了。如果你想知道为什么你的应用程序在/dev/stdin上出现管道错误后从不关闭,那就是一个bug@RobGleeson,需要与文件内容一样多的内存。。。我对上面的投票数感到惊讶。我添加了一个警告,并没有复制GB大小的文件。应该忽略fs.existsSync调用。该文件可能会在fs.existsSync调用和fs.readFileSync调用之间消失,这意味着fs.existsSync调用不会保护我们免受任何影响。此外,如果fs.existsSync失败,则返回false可能不符合人体工程学,因为copySync的消费者很少会考虑在每次调用时手动检查返回值,比我们为fs.writeFileSync等人所做的更多。。抛出异常实际上更可取。OP没有特别提到他们的文件是UTF-8文本,所以我也将从代码段中删除“UTF-8”编码,这意味着这将适用于任何文件。数据现在是一个缓冲区,而不是字符串。由于此答案是重复的,因此不进行向上投票。这是页面上唯一正确的答案。其他答案实际上都没有复制文件。MacOS和Windows上的文件还有其他元数据,这些元数据仅通过复制字节就丢失了。本页任何其他答案未复制的数据示例,以及。即使在Unix上,另一个答案也不复制创建日期,这在复制文件时通常很重要。很遗憾,这无法在mac上复制所有内容。希望他们能解决这个问题:顺便说一句,copyFile在覆盖较长的文件时会被窃听。在节点v8.7.0 libuv 1.15.0之前,由uv_fs_copyfile提供。看,这并没有回答问题,
这是关于如何在IO密集型应用程序中高效复制文件的问题。@JaredSmith是真的,但我的google搜索让我想到了这一点,这正是我想要的。我想知道为什么异步函数中的copyFileSync不能很好地执行。我认为它会被优化以匹配copyFile或流复制。这也有一个竞争条件:文件可能在统计和读/写/复制之间被销毁。最好是尝试该操作并处理由此产生的任何错误。在写入操作之前检查目标的存在性可确保您不会意外覆盖目标,例如,涵盖用户错误设置目标和源的场景相同。。。然后等待写入操作失败就太晚了。。。谁给了我-1,请审查您的排名,一旦这个事件发生在您的项目:-re。比赛-在流量大的网站上,总是建议使用单进程处理操作,需要同步保证-是的,这是性能瓶颈我没有投反对票,因为你错了,我投反对票,因为这不是问题的答案。这应该是对现有答案的一个警告性评论。好吧-你是对的,例如andrew childs的解决方案有18张投票权,将耗尽服务器/大型文件上的资源。。。我会给他写评论,但我没有评论的名声——因此你已经看到了我的帖子。。。。但是Jared你的降级对我来说意味着一个简单的方法——保持沉默,让人们编写和分享最有效的危险代码……我明白了,没有人喜欢负面反馈。但这只是一票否决票。我坚持我的理由,因为这并没有回答OP提出的问题,而且足够简短,可以作为一个评论。你可以随心所欲,但如果你把这类事情搞得不成比例,你会发现堆栈溢出是一种非常令人沮丧的经历。fs.promises.copyFileRe什么都不做/少做是做某事的最快方式:是的,的确如此。这是优化的第一条规则——消除不必要的操作。这与使现有的程序运行得更快形成对比,例如通过摆弄编译器标志。@Royi因为我想要一个异步解决方案…?还有什么其他答案?@PeterMortensen-Mike-Schilling's。
const fs = require('fs');
// File "destination.txt" will be created or overwritten by default.
fs.copyFile('source.txt', 'destination.txt', (err) => {
if (err)
throw err;
console.log('source.txt was copied to destination.txt');
});
const fs = require("fs");
fs.copyFileSync("filepath1", "filepath2"); //fs.copyFileSync("file1.txt", "file2.txt");
const fse = require('fs-extra');
let srcDir = 'path/to/file';
let destDir = 'pat/to/destination/directory';
fse.moveSync(srcDir, destDir, function (err) {
// To move a file permanently from a directory
if (err) {
console.error(err);
} else {
console.log("success!");
}
});
fse.copySync(srcDir, destDir, function (err) {
// To copy a file from a directory
if (err) {
console.error(err);
} else {
console.log("success!");
}
});
const fs = require('fs')
const copyFile = async (src, dest) => {
await fs.promises.copyFile(src, dest)
}
npx copy-speed-test --source someFile.zip --destination someNonExistentFolder