nodejs 文件的复制
在 Node.js 中,文件复制操作可以通过多种方式实现,具体取决于文件大小、性能需求以及是否需要保留文件元数据(如权限、时间戳等)。以下是几种常见的文件复制方法及其示例代码:
1. 使用 fs.copyFile
(简单高效)
这是 Node.js 提供的最简单的文件复制方法,适用于大多数场景。
const fs = require('fs');const sourcePath = 'source.txt';
const targetPath = 'destination.txt';// 异步复制(推荐)
fs.copyFile(sourcePath, targetPath, (err) => {if (err) {console.error('复制失败:', err);return;}console.log('文件复制成功');
});// 同步复制
try {fs.copyFileSync(sourcePath, targetPath);console.log('文件同步复制成功');
} catch (err) {console.error('同步复制失败:', err);
}
选项参数:
fs.constants.COPYFILE_EXCL
:若目标文件已存在则失败(不覆盖)。fs.constants.COPYFILE_FICLONE
:使用克隆(如果支持)。
// 示例:不覆盖已存在的文件
fs.copyFile(sourcePath, targetPath, fs.constants.COPYFILE_EXCL, (err) => {if (err && err.code === 'EEXIST') {console.log('目标文件已存在,跳过复制');}
});
2. 使用流(Stream)处理大文件
对于大文件,使用流可以避免内存溢出,提高效率。
const fs = require('fs');const sourcePath = 'large-file.zip';
const targetPath = 'large-file-copy.zip';// 创建可读流和可写流
const readStream = fs.createReadStream(sourcePath);
const writeStream = fs.createWriteStream(targetPath);// 管道流(自动处理数据传输和错误)
readStream.pipe(writeStream);// 监听完成和错误事件
readStream.on('error', (err) => {console.error('读取源文件失败:', err);
});writeStream.on('error', (err) => {console.error('写入目标文件失败:', err);
});writeStream.on('finish', () => {console.log('大文件复制完成');
});
更健壮的流复制实现:
async function copyFileWithStream(source, target) {return new Promise((resolve, reject) => {const readStream = fs.createReadStream(source);const writeStream = fs.createWriteStream(target);readStream.on('error', reject);writeStream.on('error', reject);writeStream.on('finish', resolve);readStream.pipe(writeStream);});
}// 使用示例
copyFileWithStream('source.txt', 'target.txt').then(() => console.log('复制成功')).catch((err) => console.error('复制失败:', err));
3. 手动读取写入(不推荐,仅作演示)
不推荐使用此方法,因为它会一次性将整个文件加载到内存中,适用于小文件。
const fs = require('fs');// 异步
fs.readFile('source.txt', (err, data) => {if (err) {console.error('读取文件失败:', err);return;}fs.writeFile('destination.txt', data, (err) => {if (err) console.error('写入文件失败:', err);else console.log('文件复制成功');});
});// 同步
try {const data = fs.readFileSync('source.txt');fs.writeFileSync('destination.txt', data);console.log('文件同步复制成功');
} catch (err) {console.error('同步复制失败:', err);
}
4. 保留文件元数据(权限、时间戳)
如果需要保留文件的原始权限和时间戳,可以在复制后设置:
const fs = require('fs');async function copyFileWithMetadata(source, target) {try {// 复制文件await fs.promises.copyFile(source, target);// 获取源文件的状态const stat = await fs.promises.stat(source);// 设置目标文件的权限await fs.promises.chmod(target, stat.mode);// 设置目标文件的时间戳await fs.promises.utimes(target, stat.atime, stat.mtime);console.log('文件复制完成,元数据已保留');} catch (err) {console.error('复制失败:', err);}
}// 使用示例
copyFileWithMetadata('source.txt', 'target.txt');
5. 递归复制目录(含子文件和子目录)
如果需要复制整个目录及其内容,可以使用递归方法:
const fs = require('fs').promises;
const path = require('path');async function copyDirectory(sourceDir, targetDir) {try {// 创建目标目录(递归选项确保父目录存在)await fs.mkdir(targetDir, { recursive: true });// 读取源目录内容const entries = await fs.readdir(sourceDir, { withFileTypes: true });// 遍历目录内容for (const entry of entries) {const sourcePath = path.join(sourceDir, entry.name);const targetPath = path.join(targetDir, entry.name);if (entry.isDirectory()) {// 递归复制子目录await copyDirectory(sourcePath, targetPath);} else {// 复制文件await fs.copyFile(sourcePath, targetPath);}}console.log(`目录 ${sourceDir} 已复制到 ${targetDir}`);} catch (err) {console.error('复制目录失败:', err);}
}// 使用示例
copyDirectory('./src', './dist');
总结
- 小文件:直接使用
fs.copyFile
。 - 大文件:使用流(
createReadStream
+createWriteStream
)。 - 递归复制目录:使用递归函数结合
fs.readdir
和fs.copyFile
。 - 保留元数据:复制后使用
chmod
和utimes
设置权限和时间戳。
根据具体需求选择合适的方法,确保处理好错误情况和边缘场景(如文件已存在、权限不足等)。