Самый быстрый способ скопировать файл в 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 это сработало сразу. Я не уверен в разнице в производительности, хотя.
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.0
API больше не является экспериментальным.
Дополнительная информация:
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');
});
Решение 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) }
в противном случае в сценарии существует риск, если источник и цель по ошибке будут заменены, ваши данные будут навсегда потеряны без уведомления об ошибке.