在node.js中复制文件的最快方法

bon*_*nez 446 javascript node.js

我正在工作的项目(node.js)意味着文件系统(复制/读/写等)的许多操作.我想知道,哪种方法最快,我很乐意提供一些建议.

小智 658

这是使用流在一行代码中复制文件的好方法:

var fs = require('fs');

fs.createReadStream('test.log').pipe(fs.createWriteStream('newLog.log'));
Run Code Online (Sandbox Code Playgroud)

在节点v8.5.0中,添加了copyFile

const fs = require('fs');

// 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');
});
Run Code Online (Sandbox Code Playgroud)

  • 请记住,在现实生活中,你想要检查`createReadStream`和`createWriteStream`是否有错误,所以你不会得到一个单行(虽然它仍然会一样快). (61认同)
  • 好吧`copy`在Window上是不可移植的,与完整的Node.js解决方案相反. (40认同)
  • 这比通过`require('child_process')执行原始的`cp test.log newLog.log`要快多少/更慢.exec`? (15认同)
  • 我使用这种方法,我得到的只是写入时的空白文件.任何想法为什么?`fs.createReadStream( './ INIT/xxx.json ')管(fs.createWriteStream(' xxx.json'));` (12认同)
  • 不幸的是,与`child_process.execFile('/ bin/cp',[' - no-target-directory',source,target])`相比,使用流的系统非常慢. (11认同)
  • @OlegMihailik两个流都默认关闭.读取流由节点关闭.如果将`{end:false}`传递给pipe,则writestream可以保持打开状态,否则默认情况下将关闭.请参见http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options (3认同)
  • @Timmerz我有同样的问题.该文件将为空白,节点将永远保存该文件的句柄.我使用了fs-extra模块的复制方法 (2认同)
  • Mikhail在下面的答案使用Node的内部`fs.copyFile`函数,是首选解决方案:/sf/answers/3237758891/ (2认同)
  • 来自项目的您好,我编辑了这个答案以反映当前的事务状态(每个非 EoL 版本的 Node.js 上都存在 copyFile) - 希望没问题。 (2认同)

Mik*_*ing 289

相同的机制,但这增加了错误处理:

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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 值得注意的是,需要cbCalled标志,因为管道错误会在两个流上触发错误.源和目标流. (5认同)
  • @SaiyanGirl'cb'代表"回调".你应该传递一个函数. (4认同)
  • 如果源文件不存在,您如何处理错误?在这种情况下仍会创建目标文件. (3认同)

Tim*_*erz 136

createReadStream/createWriteStream由于某些原因,我无法使该方法正常工作,但使用fs-extranpm模块它可以立即工作.我不确定性能差异.

FS-EXTRA

npm install --save fs-extra

var fs = require('fs-extra');

fs.copySync(path.resolve(__dirname,'./init/xxx.json'), 'xxx.json');
Run Code Online (Sandbox Code Playgroud)

  • 幸运的是,性能并不总是一个问题. (92认同)
  • 最快实施还是执行速度最快?不同的优先级意味着这是一个有效的答案. (22认同)
  • fs-extra也有异步方法,即`fs.copy(src,dst,callback);`,这些应解决@mvillar的问题. (14认同)
  • 在节点中使用同步代码会导致应用程序性能下降. (11认同)
  • 这是现在最好的选择 (3认同)
  • 哦,请...问题是关于复制文件的*最快*方法.虽然*最快*总是主观的,但我不认为同步的代码片段在这里有任何业务. (3认同)
  • @Krumia我是 - 我 - 在这样的印象下,如果我手头只有一个任务,那么如果我使用同步或异步代码就没有任何区别......只是检查我的知识,不能少...... (2认同)
  • @Paul在NodeJS生态系统中有很多用于同步代码的用例,所以我不确定我理解@Krumia的预订.绝对避免同步功能的一个地方是响应性至关重要(例如编写服务器时).即使这样,你也可以通过使用`require('child').fork(...)`来分配新的VM来利用同步代码,因为它不会阻塞主事件循环.这完全取决于背景以及您想要实现的目标. (2认同)

Mik*_*ail 122

从Node.js 8.5.0开始,我们有了新的fs.copyFilefs.copyFileSync方法.

用法示例:

var fs = require('fs');

// 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');
});
Run Code Online (Sandbox Code Playgroud)

  • 这是页面上唯一正确的答案。其他答案均未实际复制文件。MacOS和Windows上的文件还有其他元数据,这些元数据仅由于复制字节而丢失。未在此页面上的任何其他答案复制的数据示例,[windows](https://docs.microsoft.com/zh-cn/windows/desktop/fileio/file-streams)和[macos](https:// apple.stackexchange.com/questions/228444/how-do-i-create-a-fork-fork-and-store-data-in-it)。即使在Unix上,其他答案也不会复制创建日期,这在复制文件时通常很重要。 (2认同)

ben*_*eet 73

编写速度快,使用方便,具有承诺和错误管理功能.

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;
  });
}
Run Code Online (Sandbox Code Playgroud)

与async/await语法相同:

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我刚刚测试了`new Promise(函数(解析,拒绝){resolve(1); resolve(2); reject(3); reject(4); console.log("DONE");}).然后(控制台. log.bind(console),function(e){console.log("E",e);});`并查找[spec](http://people.mozilla.org/~jorendorff/es6- draft.html#sec-promise-objects)就此而言你是对的:*试图解决或拒绝已解决的承诺没有效果.*也许你可以扩展你的答案并解释你为什么用这种方式编写函数?谢谢 :-) (2认同)
  • 顺便说一句,对于可写流,`close`应该是`finish`. (2认同)

小智 41

嗯,通常最好避免异步文件操作.这是短的(即没有错误处理)同步示例:

var fs = require('fs');
fs.writeFileSync(targetFile, fs.readFileSync(sourceFile));
Run Code Online (Sandbox Code Playgroud)

  • 为什么避免异步文件操作很好? (97认同)
  • 我不知道他们被弃用了.同步方法在Web服务器上几乎总是一个糟糕的想法,但有时候在node-webkit这样的东西中是理想的,它只在文件复制时锁定窗口中的动作.抛出一个加载gif,可能是一个在某些点更新的加载栏,让sync方法阻止所有操作,直到复制完成.这不是一个真正的最佳实践,而是一个时间和地点,他们有自己的位置. (12认同)
  • @gillyb我能想到使用它们的唯一原因是为了简单 - 如果你正在编写一个只使用一次的快速脚本,你可能不会对阻止该过程感到困扰. (11认同)
  • 一般来说,这是非常错误的,特别是因为它会导致人们为每次向其服务器发出的请求重新插入文件.这可能会变得昂贵. (7认同)
  • 使用`*Sync`方法完全违背了nodejs的philosphy!我也认为他们正在慢慢被弃用.nodejs的整个想法是它是单线程和事件驱动的. (7认同)
  • 当您与另一个同步操作进行交互或者您想要执行顺序操作时,同步方法很好(即,无论如何都要模拟同步).如果操作是顺序的,只需避免回调地狱(和/或承诺汤)并使用同步方法.一般来说,它们应该在服务器上谨慎使用,但对于大多数涉及CLI脚本的情况都可以. (6认同)
  • 请记住,只有在sourceFile适合内存时才会起作用(即:不要对大文件执行此操作). (2认同)

小智 17

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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)


And*_*lds 17

如果您不关心它是异步的,并且不是在复制千兆字节大小的文件,而是宁愿不为单个函数添加另一个依赖项:

function copySync(src, dest) {
  if (!fs.existsSync(src)) {
    return false;
  }

  var data = fs.readFileSync(src, 'utf-8');
  fs.writeFileSync(dest, data);
}
Run Code Online (Sandbox Code Playgroud)

  • @RobGleeson,并且需要与文件内容一样多的内存......我对那里的upvotes计数感到惊讶. (6认同)
  • 我喜欢这个答案.清晰简单. (4认同)

Tam*_*oke 13

您可能想要使用 async/await,因为node v10.0.0内置的fs Promises API.

例子:

const fs = require('fs')

const copyFile = async (src, dest) => {
  await fs.promises.copyFile(src, dest)
}
Run Code Online (Sandbox Code Playgroud)

笔记:

node v11.14.0, v10.17.0API 不再是实验性的。

更多信息:

承诺API

承诺复制文件


小智 5

   const fs = require("fs");
   fs.copyFileSync("filepath1", "filepath2"); //fs.copyFileSync("file1.txt", "file2.txt");
Run Code Online (Sandbox Code Playgroud)

这是我个人用来复制文件并使用 Node.js 替换另一个文件的方法:)

  • 这并没有回答关于如何在 IO 密集型应用程序中有效复制文件的问题。 (2认同)