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

Javascript 在Node.js中复制文件的最快方法,javascript,node.js,Javascript,Node.js,我正在处理的项目(Node.js)意味着大量的文件系统操作(复制、读取、写入等) 哪些方法最快?使用标准内置方式: 如果您必须支持Node.js的旧版本,下面是在不支持fs.copyFile的版本中如何做到这一点: const fs = require('fs'); fs.createReadStream('test.log').pipe(fs.createWriteStream('newLog.log')); 相同的机制,但这增加了错误处理: function copyFile(source

我正在处理的项目(Node.js)意味着大量的文件系统操作(复制、读取、写入等)


哪些方法最快?

使用标准内置方式:

如果您必须支持Node.js的旧版本,下面是在不支持
fs.copyFile
的版本中如何做到这一点:

const fs = require('fs');
fs.createReadStream('test.log').pipe(fs.createWriteStream('newLog.log'));

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

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额外信息

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

fs.copySync(path.resolve(__dirname, './init/xxx.json'), 'xxx.json');

写得快,使用方便,承诺和错误管理:

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\u FICLONE
标志。它允许(支持此功能的文件系统)不实际复制文件的内容。只创建了一个新的文件条目,但它指向源文件的“克隆”

什么也不做/少做是做事最快的方式;)

让fs=require(“fs”);
fs.copyFile(
“source.txt”,
“destination.txt”,
fs.constants.COPYFILE_FICLONE,
(错误)=>{
如果(错误){
//TODO:句柄错误
控制台日志(“错误”);
}
控制台日志(“成功”);
}
);
改用承诺:

让fs=require(“fs”);
设util=require(“util”);
让copyFile=util.promisify(fs.copyFile);
复制文件(
“source.txt”,
“destination.txt”,
fs.constants.COPYFILE\u FICLONE
)
.catch(()=>console.log(“错误”))
。然后(()=>console.log(“成功”);

这就是我个人使用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,因为内置的
fs Promissions API
可以使用
节点v10.0.0

例如:

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\u process')执行原始
cp test.log newLog.log
快多少/慢多少.exec
?与完整的Node.js解决方案相反,
复制
在Windows上不可移植。不幸的是,在我的系统上,使用streams与
子进程.execFile('/bin/cp',['-无目标目录',source,target])相比,速度非常慢。
。我使用了这种方法,在写入时只得到一个空白文件。你知道为什么吗<代码>fs.createReadStream('./init/xxx.json').pipe(fs.createWriteStream('xxx.json'))这是一个很好的问题,但有趣的是,当其他类似格式的问题因为不符合所谓的“标准”而立即获得3或4张反对票时,它获得了25张赞成票(可能javascript标签是由更友善的人抓取的:),大多数情况下,我们只是对整个“文件”感到新鲜和兴奋经过多年的浏览器规范化后的业务。页面上唯一正确的答案是。其他答案实际上都没有复制文件。MacOS和Windows上的文件还有其他元数据,这些元数据仅通过复制字节就丢失了。本页任何其他答案未复制的数据示例,以及。即使在Unix上,其他答案也不会复制创建日期,这在复制文件时通常很重要。值得注意的是,需要cbCalled标志,因为管道错误会在两个流上触发错误。源和目标流。如果源文件不存在,如何处理错误?在这种情况下仍然会创建目标文件。我认为
WriteStream
中的错误只会将其取消管道。您必须自己调用
rd.destroy()
。至少我是这样的。遗憾的是,除了源代码之外,没有太多文档。cb代表什么?我们应该传递什么作为第三个参数?@SaiyanGirl'cb'代表“回调”。你应该通过一个函数
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