Самый быстрый способ скопировать файл в node.js

Проект, над которым я работаю (node.js), предполагает много операций с файловой системой (копирование / чтение / запись и т. Д.). Я хотел бы знать, какие методы являются самыми быстрыми, и я был бы рад за некоторые советы.

18 ответов

Решение

Это хороший способ скопировать файл в одну строку кода, используя потоки:

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

Тот же механизм, но это добавляет обработку ошибок:

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

Начиная с Node.js 8.5.0 у нас появились новые методы fs.copyFile и fs.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');
});

Я не смог получить createReadStream/createWriteStream метод работает по какой-то причине, но с использованием fs-extra Модуль npm это сработало сразу. Я не уверен в разнице в производительности, хотя.

фс-EXTRA

npm install --save fs-extra

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

Ну, обычно хорошо избегать асинхронных файловых операций. Вот краткий (т.е. без обработки ошибок) пример синхронизации:

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

Если вас не волнует асинхронность, и вы не копируете файлы размером в гигабайт, и не хотите добавлять другую зависимость только для одной функции:

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

  var data = fs.readFileSync(src, 'utf-8');
  fs.writeFileSync(dest, data);
}

Решение Майка Шиллинга с обработкой ошибок с ярлыком для обработчика событий ошибок.

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

Вы можете использовать async/await, так как node v10.0.0можно со встроенным fs Promises API.

Пример:

      const fs = require('fs')

const copyFile = async (src, dest) => {
  await fs.promises.copyFile(src, dest)
}

Примечание:

По состоянию на node v11.14.0, v10.17.0API больше не является экспериментальным.

Дополнительная информация:

API обещаний

Promises copyFile

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

Это то, что я лично использую для копирования файла и замены другого файла с помощью node.js:)

Для быстрых копий вы должны использовать fs.constants.COPYFILE_FICLONE флаг. Это позволяет (для файловых систем, которые поддерживают это) фактически не копировать содержимое файла. Ничего не делать - это самый быстрый способ что-то сделать;)

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

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

Почему бы не использовать 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

Решение 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);
            }
        });
    });
}

Вы можете сделать это с помощью 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!");
    }
});

Или же

fse.copySync(srcDir, destDir, function (err) {

     // To copy a file from a directory
     if (err) {
         console.error(err);
     } else {
         console.log("success!");
     }
});

Я написал небольшую утилиту для проверки различных методов:

https://www.npmjs.com/package/copy-скорость-тест

запустить его с

      npx copy-speed-test --source someFile.zip --destination someNonExistentFolder

Он выполняет собственную копию с помощью child_process.exec(), файл копии с помощью fs.copyFile и использует createReadStream с различными размерами буфера (вы можете изменить размеры буфера, передав их в командной строке. запустите npx copy-speed- test -h для получения дополнительной информации).

Улучшение еще одного ответа.

Особенности:

  • Если папки 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);
}

Решение Майка, но с обещаниями:

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

Все вышеупомянутые решения, которые не проверяют существование исходного файла, опасны... например

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

в противном случае в сценарии существует риск, если источник и цель по ошибке будут заменены, ваши данные будут навсегда потеряны без уведомления об ошибке.

Другие вопросы по тегам