node.js에서 파일을 복사하는 가장 빠른 방법


488

내가 작업중 인 프로젝트 (node.js)는 파일 시스템 (복사 / 읽기 / 쓰기 등)으로 많은 작업을 의미합니다. 어떤 방법이 가장 빠른지 알고 싶습니다. 기꺼이 조언을 구하십시오. 감사.


42
다른 유사한 형식의 질문은 어쩌면 자바 스크립트 태그가 친절 명 : 크롤링하는 SO "표준"(충족하지에 대한 바로 3 또는 4 downvotes를 얻을 것이다 때 25 upvotes을 얻는 것은 흥미 롭다하지만 그것은 좋은 질문이다

22
우리는 몇 년 동안 브라우저를 정규화 한 후이 전체 "파일"비즈니스에 대해 신선하고 신선합니다.
Erik Reppen

3
이 페이지의 유일한 정답은 이것 입니다. 다른 답변은 실제로 파일을 복사하지 않습니다. MacOS 및 Windows의 파일에는 바이트 복사만으로 손실되는 다른 메타 데이터가 있습니다. 이 페이지의 다른 답변, macos 에 의해 복사되지 않은 데이터의 예 . 유닉스에서도 다른 답변은 작성 날짜를 복사하지 않으며 파일을 복사 할 때 종종 중요합니다.
gman

답변:


717

이것은 스트림을 사용하여 한 줄의 코드로 파일을 복사하는 좋은 방법입니다.

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
실생활에서 createReadStreamcreateWriteStream오류를 모두 확인하고 싶기 때문에 하나의 라이너를 얻지 못할 것입니다 (여전히 빠르지 만).
ebohlman

18
raw cp test.log newLog.logvia를 실행하는 것보다 얼마나 빠르거나 느립니다 require('child_process').exec?
랜스 폴라드

41
글쎄 copy, 창에 휴대용 전체 Node.js를 솔루션에 위배되지 않습니다.
Jean

12
불행히도 스트림을 사용하는 시스템에서에 비해 속도가 매우 느립니다 child_process.execFile('/bin/cp', ['--no-target-directory', source, target]).
Robert

12
나는이 방법을 사용했으며 내가 쓰는 것은 빈 파일이었습니다. 왜 어떤 아이디어? fs.createReadStream('./init/xxx.json').pipe(fs.createWriteStream('xxx.json'));
Timmerz

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 플래그가 필요하다는 점에 주목할 가치가 있습니다. 소스 및 대상 스트림.
Gaston Sanchez

4
소스 파일이 없으면 오류를 어떻게 처리합니까? 이 경우 대상 파일이 여전히 생성됩니다.
Michel Hua

1
WriteStream의지 의 오류는 파이프를 풀어야 한다고 생각 합니다. 당신은 rd.destroy()자신 을 호출해야합니다 . 적어도 그것이 저에게 일어난 일입니다. 슬프게도 소스 코드를 제외하고는 문서가 많지 않습니다.
Robert

무엇 cb을 의미합니까? 세 번째 논쟁으로 무엇을 전달해야합니까?
SaiyanGirl

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

14
fs-extra에는 비동기 메소드가 있습니다. 즉 fs.copy(src, dst, callback);, @mvillar의 문제를 해결해야합니다.
Marc Durdin

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의 파일에는 바이트 복사만으로 손실되는 다른 메타 데이터가 있습니다. 이 페이지의 다른 답변, macos 에 의해 복사되지 않은 데이터의 예 . 유닉스에서도 다른 답변은 생성 날짜를 복사하지 않습니다. 파일을 복사 할 때 종종 중요합니다.
gman

슬프게도 이것은 Mac의 모든 것을 복사하지 못합니다. 바라건대 그들은 그것을 고칠 것이다 : github.com/nodejs/node/issues/30575
G 맨

BTW copyFile()는 더 긴 파일을 덮어 쓰는 동안 버그가 있음을 명심 하십시오. 씨의 uv_fs_copyfile()노드 v8.7.0 (libuv 1.15.0)까지. 참조 github.com/libuv/libuv/pull/1552
안톤 Rudeshko

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
더 이상 입력이 없으면 (네트워크 공유 끊김) 쓰기가 계속 성공하면 어떻게됩니까? 읽기 (읽기)와 해결 (쓰기)이 모두 호출됩니까? 읽기 / 쓰기가 모두 실패하면 (읽기 중 불량 디스크 섹터, 쓰기 중 전체 디스크)? 그런 다음 거부가 두 번 호출됩니다. 플래그를 사용하여 Mike의 답변을 기반으로 한 Promise 솔루션은 (불행히도) 오류 처리를 올바르게 고려하는 유일한 실행 가능한 솔루션 인 것 같습니다.
Lekensteyn

복사가 성공하면 약속이 해결됩니다. 거부되면 상태가 정해지고 거부를 여러 번 호출해도 아무런 차이가 없습니다.
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

11
@gillyb 내가 그것들을 사용한다고 생각할 수있는 유일한 이유는 간단하기 때문입니다. 한 번만 사용할 빠른 스크립트를 작성하는 경우 프로세스 차단에 대해 신경 쓰지 않을 것입니다.
starbeamrainbowlabs

13
더 이상 사용되지 않는 것으로 알고 있습니다. 동기화 방법은 거의 항상 웹 서버에서 끔찍한 아이디어이지만 파일을 복사하는 동안 창에서 작업을 잠그는 node-webkit과 같은 경우에 이상적입니다. 로딩 gif 및 특정 지점에서 업데이트되는로드 바를 던져서 복사가 완료 될 때까지 sync 메소드가 모든 작업을 차단하도록합니다. 장소와 장소가 언제 어디서나 가장 좋은 방법은 아닙니다.
Erik Reppen

6
동기화 방법은 다른 동기화 작업과 상호 작용할 때 또는 원하는 작업이 순차적 작업을 수행하는 것 (즉, 동기화를 에뮬레이션하는 것)에 적합합니다. 작업이 순차적이면 콜백 지옥 (및 약속 수프)을 피하고 sync 메소드를 사용하십시오. 일반적으로 서버에서는주의해서 사용해야하지만 CLI 스크립트와 관련된 대부분의 경우에는 적합합니다.
srcspider

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, 그리고 파일 내용만큼 많은 메모리가 필요합니다.
Konstantin

"기가 바이트 크기의 파일을 복사하지 않습니다"라는 경고를 추가했습니다.
Andrew Childs

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 True,하지만 내 Google 검색으로 나를 여기로 안내하고 이것이 내가 원하는 것입니다.
codepleb

1

빠른 복사를 위해서는 fs.constants.COPYFILE_FICLONE플래그를 사용해야합니다 . (이를 지원하는 파일 시스템의 경우) 실제로 파일의 내용을 복사하지 못하게합니다. 새 파일 항목 만 작성되지만 소스 파일 의 Copy-on-Write "복제본"을 가리 킵니다 .

아무것도하지 않거나 덜하는 것은 무언가를하는 가장 빠른 방법입니다.)

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

대신 약속을 사용하십시오.

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

-1

Mike의 솔루션 이지만 약속이 있습니다.

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

다른 답변의 개선.

풍모:

  • 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)이 사건이 당신의 프로젝트에서 발생하면 당신의 순위를 검토하십시오 :-) re. 경쟁이 심한 지역에서는 항상 동기화 보증이 필요한 하나의 프로세스 처리 작업을 수행하는 것이 좋습니다. 예, 성능 병목 현상이 발생합니다
stancikcom

나는 당신이 렸기 때문에 downvote하지 않았습니다. 이것은 이것이 질문에 대한 대답이 아니기 때문에 downvoted입니다. 기존 답변에주의해야합니다.
Jared Smith

잘-당신은 예를 들어 앤드류 차일드 솔루션 (18 upvotes 포함)은 서버 / 큰 파일의 리소스에 부족할 것입니다 ... 나는 그에게 의견을 쓸 것입니다. 그러나 나는 논평 할 평판이 없습니다. ... 그러나 당신의 다운 그레이드는 저에게 간단한 진전을 의미합니다-침묵을 지키고 사람들이 주로 "작동하는"위험한 코드를 작성하고 공유하게하십시오 ...
stancikcom

나는 그것을 얻는다, 아무도 부정적인 피드백을 좋아하지 않는다 . 그러나 그것은 단지 공감입니다. OP가 요청한 질문에 대한 답변이 아니며 의견을 제출할 수있을 정도로 짧기 때문에 본인이 제공 한 이유가 있습니다. 원하는대로 가져갈 수는 있지만 그러한 종류의 비율을 비례 적으로 불면 스택 오버플로가 매우 실망스러운 경험이 될 것입니다.
Jared Smith
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.