node.jsでファイルをコピーする最速の方法


488

私が取り組んでいるプロジェクト(node.js)は、ファイルシステムでの多くの操作(コピー/読み取り/書き込みなど)を意味します。どの方法が最速か知りたいのですが、アドバイスをいただければ幸いです。ありがとう。


42
これは良い質問ですが、SOの「標準」を満たしていないために他の同様の形式の質問がすぐに3または4の反対票を投じるときに25票を獲得するのは興味深いことです(おそらくJavaScriptタグは親切な人々によってクロールされます:)
Ben

22
ほとんどの場合、何年にもわたってブラウザを正規化してきた結果、この「ファイル」ビジネス全体に新鮮でワクワクしています。
Erik Reppen 2014年

3
ページ上の唯一の正解はこれです。他の答えはどれも実際にファイルをコピーしません。MacOSおよびWindows上のファイルには、バイトをコピーするだけで失われる他のメタデータがあります。このページ、windowsmacosの他の回答でコピーされていないデータの例。Unixでも、他の回答は作成日をコピーしません。これは、ファイルをコピーするときに重要になることがよくあります。
gman 2018年

回答:


717

これは、ストリームを使用して1行のコードでファイルをコピーするのに適した方法です。

var fs = require('fs');

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

ノード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');
});

64
実際の生活では、createReadStreamとの両方のcreateWriteStreamエラーをチェックする必要があるので、ワンライナーは得られないことを覚えておいてください(それでも速度は同じです)。
ebohlman 2012

18
これはraw cp test.log newLog.logviaを実行するよりもどのくらい速く/遅くなりrequire('child_process').execますか?
ランス

41
まあcopy、完全なNode.jsソリューションとは異なり、Windowには移植できません。
Jean

12
残念ながら、私のシステムでは、ストリームを使用するとに比べて非常に遅くなり child_process.execFile('/bin/cp', ['--no-target-directory', source, target])ます。
ロバート

12
この方法を使用したところ、書き込み時に空のファイルができました。なぜ何かアイデア?fs.createReadStream('./init/xxx.json').pipe(fs.createWriteStream('xxx.json'));
Timmerz 2014

293

同じメカニズムですが、これによりエラー処理が追加されます。

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

5
パイプエラーが両方のストリームでエラーをトリガーするため、cbCalledフラグが必要であることは注目に値します。ソースおよび宛先ストリーム。
ガストンサンチェス

4
ソースファイルが存在しない場合、エラーをどのように処理しますか?その場合、宛先ファイルは引き続き作成されます。
ミシェル・華

1
のエラーはそれWriteStreamをアンパイプするだけだと思います。あなたはrd.destroy()自分を呼ばなければならないでしょう。少なくともそれが私に起こったことです。悲しいことに、ソースコード以外のドキュメントはあまりありません。
Robert

何のcb略ですか?3番目の引数として何を渡すべきですか?
SaiyanGirl 2015

4
@SaiyanGirl 'cb'は「コールバック」の略です。関数を渡す必要があります。
ブライアンJ.ミラー

143

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

3
これは今のところ最良の選択肢です
Zain Rizvi

11
ノードで同期コードを使用すると、アプリケーションのパフォーマンスが低下します。
mvillar

3
ああお願い... ファイルをコピーする最速の方法についての質問です。一方で、最速は常に主観的ですが、私はコードの同期部分は、ここでどのようなビジネスを持っているとは思いません。
sampathsris

24
実装が最速か、実行が最速か?優先順位の違いは、これが有効な答えであることを意味します。
Patrick Gunderson、2015

14
fs-extraにも非同期メソッドがあります。つまりfs.copy(src, dst, callback);、これらは@mvillarの懸念を解決するはずです。
Marc Durdin、2015年

134

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

2
これがページでの唯一の正解です。他の答えはどれも実際にファイルをコピーしません。MacOSおよびWindows上のファイルには、バイトをコピーするだけで失われる他のメタデータがあります。このページ、windowsmacosの他の回答でコピーされていないデータの例。Unixでも、他の答えは作成日をコピーしません。これは、ファイルをコピーするときに重要になることがよくあります。
gman 2018年

残念ながら、これはMacですべてをコピーできません。うまくいけば彼らはそれを修正するでしょう:github.com/nodejs/node/issues/30575
gman

ところで、copyFile()より長いファイルを上書きしている間はバグがあることに注意してください。uv_fs_copyfile()Node v8.7.0(libuv 1.15.0)までの礼儀。github.com/libuv/libuv/pull/1552を
アントンルデシュコ

74

プロミスとエラー管理を備えた、書き込みが高速で使いやすい。

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

1
入力がなくなった(ネットワーク共有が壊れた)にもかかわらず、書き込みが成功した場合はどうなりますか?拒否(読み取りから)と解決(書き込みから)の両方が呼び出されますか?読み取り/書き込みの両方が失敗した場合(読み取り中に不良ディスクセクター、書き込み中にフルディスク)次に、rejectが2回呼び出されます。フラグ付きのマイクの回答に基づくPromiseソリューション(残念ながら)は、エラー処理を適切に考慮する唯一の実行可能なソリューションのようです。
Lekensteyn

コピーが成功すると、約束は解決されます。拒否された場合、その状態は解決され、rejectを複数回呼び出しても違いはありません。
benweet

2
私はこれについてテストnew Promise(function(resolve, reject) { resolve(1); resolve(2); reject(3); reject(4); console.log("DONE"); }).then(console.log.bind(console), function(e){console.log("E", e);});して仕様を調べましたが、あなたは正しいです:解決された約束を解決または拒否しようとしても効果はありません。おそらくあなたはあなたの答えを拡張して、なぜあなたがこのように関数を書いたのかを説明できますか?ありがとう:-)
Lekensteyn

2
ちなみに、書き込み可能なストリームのcloseはずfinishです。
Lekensteyn

そして、アプリケーションがのパイプエラーの後に閉じないのはなぜか疑問に思うなら/dev/stdin、それはバグですgithub.com/joyent/node/issues/25375
Lekensteyn

43

まあ、通常は非同期のファイル操作を避けるのが良いでしょう。以下は、短い(つまり、エラー処理を行わない)同期の例です。

var fs = require('fs');
fs.writeFileSync(targetFile, fs.readFileSync(sourceFile));

8
これは一般的に非常に誤りです。特に、サーバーに対して行われるすべての要求に対して、人々がファイルを丸呑みすることになるためです。これは高価になる可能性があります。
Catalyst

8
*Syncメソッドの使用は、nodejsの哲学に完全に反します!また、これらは徐々に廃止されていると思います。nodejsの全体的な考え方は、それがシングルスレッドでイベント駆動型であるということです。
gillyb 2014年

11
@gillyb私がそれらを使用するために考えることができる唯一の理由は、単純化のためです-一度だけ使用する簡単なスクリプトを書いている場合、おそらくプロセスのブロックに悩まされることはおそらくないでしょう。
starbeamrainbowlabs 2014年

13
私はそれらが廃止されていることを知りません。同期メソッドは、ほとんどの場合Webサーバーではひどい考えですが、ファイルのコピー中にウィンドウ内のアクションのみをロックするnode-webkitなどの場合に理想的です。ローディングGIFをスローし、特定のポイントで更新するロードバーをスローし、コピーが完了するまで同期メソッドがすべてのアクションをブロックするようにします。いつ、どこに場所があるのと同じくらい、それは実際にはベストプラクティスではありません。
Erik Reppen 2014年

6
同期方法は、別の同期操作とやり取りしている場合や、シーケンシャル操作を実行したい場合(つまり、同期をエミュレートする場合)に適しています。操作がシーケンシャルである場合は、コールバック地獄(および/またはpromiseスープ)を避け、syncメソッドを使用します。一般に、サーバーでは注意して使用する必要がありますが、CLIスクリプトが関係するほとんどの場合は問題ありません。
srcspider 2015

18

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

18

非同期であることを気にせず、ギガバイトサイズのファイルをコピーしておらず、単一の関数のためだけに別の依存関係を追加したくない場合:

function copySync(src, dest) {
  var data = fs.readFileSync(src);
  fs.writeFileSync(dest, data);
}

4
私はこの答えが好きです。明確でシンプル。
Rob Gleeson

7
@RobGleeson、そしてファイルのコンテンツと同じくらい多くのメモリを必要とします...私はそこにある賛成投票の数に驚いています。
コンスタンティン

「ギガバイトサイズのファイルをコピーしない」という警告を追加しました。
アンドリューチャイルズ

fs.existsSync呼び出しは省略されなければなりません。fs.existsSync通話と通話の間にファイルが消える可能性がありますfs.readFileSync。つまり、fs.existsSync通話は何からも保護されません。
qntm

また、帰国false場合はfs.existsSync失敗する可能性があるの少数の消費者が悪いために人間工学は、copySync任意のより多くの我々が行うよりも、手動で戻り値にそれが呼ばれていますたびに検査することだと思いますfs.writeFileSync ら。。例外を投げることが実際に望ましいです。
qntm

2
   const fs = require("fs");
   fs.copyFileSync("filepath1", "filepath2"); //fs.copyFileSync("file1.txt", "file2.txt");

これは私が個人的にファイルをコピーし、node.jsを使用して別のファイルを置き換えるために使用するものです:)


1
これは、IO負荷の高いアプリケーションでファイルを効率的にコピーする方法に関する質問には答えません。
Jared Smith

@JaredSmith真実ですが、私のグーグル検索が私をここに導き、これが私が欲しかったものです。
codepleb

1

高速コピーには、fs.constants.COPYFILE_FICLONEフラグを使用する必要があります。(これをサポートするファイルシステムの場合)実際にはファイルの内容をコピーしないようにすることができます。新しいファイルエントリが作成されるだけですが、それはソースファイルのコピーオンライト「クローン」を指します。

何もしない/少ないことは、何かをする最も速い方法です;)

https://nodejs.org/api/fs.html#fs_fs_copyfile_src_dest_flags_callback

let fs = require("fs");

fs.copyFile(
  "source.txt",
  "destination.txt",
  fs.constants.COPYFILE_FICLONE,
  (err) => {
    if (err) {
      // TODO: handle error
      console.log("error");
    }
    console.log("success");
  }
);

代わりにpromiseを使用する:

let fs = require("fs");
let util = require("util");
let copyFile = util.promisify(fs.copyFile);


copyFile(
  "source.txt",
  "destination.txt",
  fs.constants.COPYFILE_FICLONE
)
  .catch(() => console.log("error"))
  .then(() => console.log("success"));

fs.promises.copyFile
gman

0

コピーの前にファイルの可視性をチェックするbenweetのソリューション:

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

0

コピー機能に組み込まれたnodejsを使用しないのはなぜですか?

非同期バージョンと同期バージョンの両方を提供します。

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

https://nodejs.org/api/fs.html#fs_fs_copyfilesync_src_dest_flags


3
この回答は重複しているため、賛成票を投じません。
Qwertie 2018

-1

マイクのソリューション、ただし約束あり:

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

@Royi非同期ソリューションが欲しかったので...?
mpen

-1

他の1つの答えの改善。

特徴:

  • dstフォルダーが存在しない場合は、自動的に作成されます。他の答えはエラーを投げるだけです。
  • これはを返しますpromise。これにより、大規模なプロジェクトでの使用が容易になります。
  • それはあなたが複数のファイルをコピーすることを可能にし、それらのすべてがコピーされたときに約束が行われます。

使用法:

var onePromise = copyFilePromise("src.txt", "dst.txt");
var anotherPromise = copyMultiFilePromise(new Array(new Array("src1.txt", "dst1.txt"), new Array("src2.txt", "dst2.txt")));

コード:

function copyFile(source, target, cb) {
    console.log("CopyFile", source, target);

    var ensureDirectoryExistence = function (filePath) {
        var dirname = path.dirname(filePath);
        if (fs.existsSync(dirname)) {
            return true;
        }
        ensureDirectoryExistence(dirname);
        fs.mkdirSync(dirname);
    }
    ensureDirectoryExistence(target);

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

function copyFilePromise(source, target) {
    return new Promise(function (accept, reject) {
        copyFile(source, target, function (data) {
            if (data === undefined) {
                accept();
            } else {
                reject(data);
            }
        });
    });
}

function copyMultiFilePromise(srcTgtPairArr) {
    var copyFilePromiseArr = new Array();
    srcTgtPairArr.forEach(function (srcTgtPair) {
        copyFilePromiseArr.push(copyFilePromise(srcTgtPair[0], srcTgtPair[1]));
    });
    return Promise.all(copyFilePromiseArr);
}

-2

ソースファイルの存在をチェックしない上記のすべてのソリューションは危険です...例

fs.stat(source, function(err,stat) { if (err) { reject(err) }

そうしないと、ソースとターゲットが誤って置き換えられた場合にシナリオにリスクが生じ、エラーに気付かずにデータが完全に失われます。


これには競合状態もあります。つまり、ファイルのステータスと読み取り/書き込み/コピーの間でファイルが破壊される可能性があります。操作を試し、エラーが発生した場合は対処することをお勧めします。
Jared Smith

書き込み操作の前にターゲットの存在を確認することで、誤ってターゲットを上書きしないようにします。たとえば、宛先とソースがユーザーによって同じように誤って設定された場合などです...書き込み操作が失敗するのを待つのは遅れます... (-1)プロジェクトでこのインシデントが発生したら、ランキングを確認してください:-)再。競合-交通量の多いサイトでは、同期の保証を必要とする操作を処理する1つのプロセスを
用意

あなたが間違っているので私は反対票を投じませんでした。これは質問への回答ではないため、反対票を投じました。これは、既存の回答に対する警告コメントでなければなりません。
Jared Smith

まあ-あなたは正しい(例:アンドリューチャイルズソリューション(18票))がサーバー/大きなファイルのリソースで不足します...私は彼にコメントを書きますが、コメントする評判がないので、私の投稿をスタンドアロンで見ました。 ...しかし、ダウングレードされたJaredは私にとって簡単な道を意味します-黙って、ほとんどの場合「機能する」危険なコードを人々が書いて共有できるようにします...
stancikcom

私はそれを得ます、誰も負帰還が好きではありません。しかし、それは単なる反対票です。これはOPが尋ねた質問に答えず、コメントにするのに十分短いので、私はそれを与える理由を受け入れます。あなたは好きなようにそれを取ることができますが、そのようなことを不均衡に吹き飛ばすと、スタックオーバーフローが非常に苛立たしい経験であることがわかります。
Jared Smith
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.