复制
简单而强大的复制工具 .
复制工具递归复制文件,目录,以及从源目录到目的目录的链接.
特征 :
- 重命名和移动文件和目录
- 过滤路径
- 文件内容的转换
- 在每个路径复制后执行动作
- 保留用户权限
- 覆盖或保存已存在文件
- 创建或调试链接
- 保留原始时间戳
- 收集结果( 目录、文件和链接的大小和数量)
- 不写入文件进行干线运行
- 可用作 fs-extra/拷贝,参见 选项
复制工具故意没有
- 提供同步 API
- 检查周期, 即如果目的地是源的子目录
- 提供基于流和块的变换 API
- 保存链接的时间戳( 因为节点不为此提供OS 独立的 API)
- 如果源和(或)目的地被重新命名或移动,则重写符号链接的相对链接
安装
npm i @danieldietrich/copy
使用量
该模块支持ES6 导入 和通用JS系统 需求 样式.
import copy from '@danieldietrich/copy';
(async function() {
// Performs a dry run of copying ./node_modules to ./temp
const totals = await copy('node_modules', 'temp', { dryRun: true });
console.log('Totals:', totals);
})();
共计 包含复制操作信息 :
{
directories: 1338,
files: 7929,
symlinks: 48,
size: 87873775
}
这个 编号 目录、文件和同义链接对应源。 这个 大小 反映写入字节的数目。 特别是,如果现有文件没有被删除,其大小可能小于来源.
实例
使用复制操作
另见 选项优先。 。 。 .
const copy = require('@danieldietrich/copy');
const path = require('path');
(async function() {
// move dist/ dir contents to parent dir and rename index.js files to index.mjs
const rename = (source, target) => {
if (source.stats.isDirectory() && source.path.endsWith('/dist')) {
return path.dirname(target.path);
} else if (source.stats.isFile() && source.path.endsWith('/index.js')) {
return path.join(path.dirname(target.path), 'index.mjs');
} else {
return;
}
};
// recursively copy all .js files
const filter = (source, target) =>
source.stats.isDirectory() || source.stats.isFile() && source.path.endsWith('.js');
// transform the contents of all index.mjs files to upper case
const transform = (data, source, target) => {
if (source.stats.isFile() && target.path.endsWith('/index.mjs')) {
return Buffer.from(data.toString('utf8').toUpperCase(), 'utf8');
} else {
return data;
}
};
// log some information about copied files
const afterEach = (source, target, options) => {
const dryRun = options.dryRun ? '[DRY RUN] ' : '';
if (source.stats.isDirectory()) {
console.log(`${dryRun}Created ${target.path}`);
} else {
// target.stats is undefined on a dry run if the target does not already exist!
const size = target.stats?.size || '?';
console.log(`${dryRun}Copied ${source.path} to ${target.path} (${size} bytes)`);
}
};
const totals = await copy('node_modules', 'temp', {
rename,
filter,
transform,
afterEach
});
console.log('Totals:', totals);
})();
更改文件属性
在下面的例子中,我们更改文件所有者 uid 。 chgrp或chmod可能以类似方式进行.
import * as copy from '@danieldietrich/copy';
import * as fs from 'fs';
const { lchown } = fs.promises;
async function changeOwner(src: string, dst: string, uid: number) {
copy(src, dst, {
afterEach: async (source, target) => {
lchown(target.path, uid, source.stats.gid);
}
});
}
执行进展指标
import * as copy from '@danieldietrich/copy';
async function copyWithProgress(src: string, dst: string, callback: (curr: copy.Totals, sum: copy.Totals) => void) {
const curr: copy.Totals = {
directories: 0,
files: 0,
symlinks: 0,
size: 0
};
const sum = await copy(src, dst, { dryRun: true });
const interval = 100; // ms
let update = Date.now();
await copy(src, dst, { afterEach: (source) => {
if (source.stats.isDirectory()) {
curr.directories += 1;
} else if (source.stats.isFile()) {
curr.files += 1;
curr.size += source.stats.size;
} else if (source.stats.isSymbolicLink()) {
curr.symlinks += 1;
curr.size += source.stats.size;
}
if (Date.now() - update >= interval) {
update = Date.now();
callback(curr, sum);
}
}});
callback(sum, sum);
}
// usage
(async function() {
copyWithProgress('node_modules', 'temp', (curr, sum) => {
const progress = Math.min(100, Math.floor(curr.size / sum.size * 100));
console.log(`${Number.parseFloat(progress).toFixed(1)} %`);
});
})();
API 密码
普遍签署: 复制 为:
async function copy(sourcePath: string, targetPath: string, options?: copy.Options): Promise<copy.Totals>;
公共类型有:
// compatible to fs-extra.copy
type Options = {
overwrite?: boolean;
errorOnExist?: boolean;
dereference?: boolean;
preserveTimestamps?: boolean;
dryRun?: boolean;
rename?: (source: Source, target: Target, options: Options) => string | void | Promise<string | void>;
filter?: (source: Source, target: Target, options: Options) => boolean | Promise<boolean>;
transform?: (data: Buffer, source: Source, target: Target, options: Options) => Buffer | Promise<Buffer>;
afterEach?: (source: Source, target: Target, options: Options) => void | Promise<void>;
};
type Source = {
path: string;
stats: fs.Stats;
};
type Target = {
path: string;
stats?: fs.Stats;
};
type Totals = {
directories: number;
files: number;
symlinks: number;
size: number; // not size on disk in blocks
};
选项
复制是Fs-extra/复制的超级集. 选项名称和默认值对应fs-extra/复制选项.
| 选项 | 说明 |
|---|---|
| 覆盖 | 设置到时保存扩展文件 虚假默认 : 真实 |
| 存在时出错 | 与 覆盖: 假默认 : 虚假 |
| 调试 | 复制文件,如果 真实默认 : 虚假 |
| 保存时间戳 | 保留了原来的时间戳. 默认 : 虚假 |
| 干流页:1 | 不执行任何写操作. 之后的每个 被调用,需要检查 选项. dryRun默认 : 虚假 |
| 重命名页:1 | 可选重命名函数 。 目标路径在返回非空时重新命名 字符串,否则取出原名。 当将目录移动到不同的位置时,内部可能会使用递归式mkdir. 在这种情况下,至少是节点 第10.12条 需要时间. |
| 过滤器 | 可选路径过滤器 。 返回时排除路径 虚假 并列入 真实。 。 。 . |
| 转换页:1 | 文件内容的可选转换 . |
| 之后的每个页:1 | 在路径被复制后执行的可选动作,甚至在干线上进行. 请检查 选项. dryRun 和/或如果 目标. 数据 定义. |
*) fs-extra没有这个特性
选项优先
- 首先,目标路径通过调用 重命名 函数,如果存在。 请注意,当前的目标路径被传递到函数上.
- 然后选择权 过滤器 函数被应用。 因为我们在目标被重新命名后就这样做了,所以我们能够考虑到现有目标的内容.
- 接下来,可选 转换 函数调用.
- 目标写完后,可选 之后的每个 函数调用.