npm package: @nijiiro/verteiler
Description: 将大目录中的文件按大小限制拆分到多个子目录中
Author: nijiirono
Latest version: 0.2.8

# Verteiler

一个Node.js工具，用于将大目录中的文件按大小限制拆分到多个子目录中。支持命令行使用和编程式API调用。

## 功能特性

- 📁 **智能文件拆分**: 按指定大小限制将文件分配到多个子目录
- 🔄 **回退操作**: 支持将拆分后的文件移回原始目录
- 📦 **目录压缩**: 可选的ZIP压缩功能，节省存储空间
- ⚙️ **灵活配置**: 支持自定义文件扩展名、目录前缀、大小限制等
- 📊 **详细日志**: 完整的操作日志和进度显示
- 🛠️ **双重使用方式**: 支持命令行工具和编程式API

## 安装

### 全局安装（推荐）

```bash
npm install -g @nijiiro/verteiler
```

### 本地安装

```bash
npm install @nijiiro/verteiler
```

### 开发安装

```bash
git clone <repository-url>
cd verteiler
npm install
npm link  # 用于本地开发测试
```

## 使用方法

### 命令行使用

#### 基本用法

```bash
# 拆分指定目录中的文件
verteiler /path/to/your/directory

# 指定目录大小限制
verteiler /path/to/your/directory --max-size 500MB

# 指定目标文件扩展名
verteiler /path/to/your/directory --extensions .jpg,.png,.rw2

# 自定义目录前缀
verteiler /path/to/your/directory --prefix photos
```

#### 高级选项

```bash
# 启用目录压缩
verteiler /path/to/your/directory --compress-dirs

# 设置压缩级别（1-9，9为最高压缩率）
verteiler /path/to/your/directory --compress-dirs --compression-level 9

# 回退操作（将文件移回源目录）
verteiler /path/to/your/directory --rollback
```

#### 完整示例

```bash
# 将照片目录按500MB限制拆分，启用压缩
verteiler /Users/username/Photos \
  --max-size 500MB \
  --extensions .jpg,.jpeg,.png,.rw2 \
  --prefix photos \
  --compress-dirs \
  --compression-level 6
```

### 编程式API使用

```javascript
const { FileSplitter } = require('@nijiiro/verteiler');

// 创建实例
const splitter = new FileSplitter({
  MAX_DIR_SIZE: 1024 * 1024 * 1024, // 1GB
  TARGET_EXTENSIONS: ['.jpg', '.png', '.rw2'],
  DIR_PREFIX: 'photos',
  COMPRESS_DIRS: {
    ENABLED: true,
    LEVEL: 6,
    DELETE_ORIGINAL: true
  }
});

// 执行拆分
async function splitFiles() {
  try {
    const result = await splitter.splitFiles('/path/to/directory');
    console.log(`成功处理 ${result.processedFiles} 个文件`);
    console.log(`创建 ${result.directoriesCreated} 个目录`);
  } catch (error) {
    console.error('拆分失败:', error.message);
  }
}

// 执行回退
async function rollbackFiles() {
  try {
    await splitter.rollback('/path/to/directory');
    console.log('回退操作完成');
  } catch (error) {
    console.error('回退失败:', error.message);
  }
}
```

## 配置选项

### 默认配置

```javascript
{
  MAX_DIR_SIZE: 1024 * 1024 * 1024, // 1GB
  TARGET_EXTENSIONS: ['.RW2', '.rw2'],
  DIR_PREFIX: 'dir',
  PRESERVE_TIMESTAMPS: true,
  COMPRESS_DIRS: {
    ENABLED: false,
    LEVEL: 6,
    DELETE_ORIGINAL: true
  }
}
```

### 配置说明

| 选项 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `MAX_DIR_SIZE` | number | 1GB | 每个目录的最大大小限制（字节） |
| `TARGET_EXTENSIONS` | string[] | ['.RW2', '.rw2'] | 目标文件扩展名列表 |
| `DIR_PREFIX` | string | 'dir' | 输出目录的前缀 |
| `PRESERVE_TIMESTAMPS` | boolean | true | 是否保持文件的时间戳 |
| `COMPRESS_DIRS.ENABLED` | boolean | false | 是否启用目录压缩 |
| `COMPRESS_DIRS.LEVEL` | number | 6 | 压缩级别（1-9） |
| `COMPRESS_DIRS.DELETE_ORIGINAL` | boolean | true | 压缩后是否删除原始目录 |

## API 参考

### FileSplitter 类

#### 构造函数

```javascript
new FileSplitter(config)
```

- `config` (Object): 配置对象，可选

#### 方法

##### `splitFiles(sourceDir, options)`

执行文件拆分操作。

- `sourceDir` (string): 源目录路径
- `options` (Object): 额外选项，可选
- 返回: `Promise<Object>` 操作结果

##### `rollback(sourceDir)`

执行回退操作，将文件移回源目录。

- `sourceDir` (string): 源目录路径
- 返回: `Promise<Object>` 操作结果

##### `scanTargetFiles(sourceDir)`

扫描目标文件。

- `sourceDir` (string): 源目录路径
- 返回: `Array<Object>` 文件信息数组

##### `formatBytes(bytes)`

格式化字节数为可读格式。

- `bytes` (number): 字节数
- 返回: `string` 格式化后的字符串

##### `parseSize(sizeStr)`

解析大小字符串为字节数。

- `sizeStr` (string): 大小字符串（如 "1GB", "500MB"）
- 返回: `number` 字节数

### 工具函数

```javascript
const { formatBytes, parseSize, DEFAULT_CONFIG } = require('@nijiiro/verteiler');
```

## 命令行选项

| 选项 | 说明 | 示例 |
|------|------|------|
| `--max-size <大小>` | 目录大小限制 | `--max-size 500MB` |
| `--extensions <扩展名>` | 目标文件扩展名 | `--extensions .jpg,.png` |
| `--prefix <前缀>` | 输出目录前缀 | `--prefix photos` |
| `--compress-dirs` | 启用目录压缩 | `--compress-dirs` |
| `--compression-level <级别>` | 压缩级别 | `--compression-level 9` |
| `--rollback` | 回退操作 | `--rollback` |
| `--help` | 显示帮助信息 | `--help` |

## 依赖项

### 必需依赖

- Node.js >= 12.0.0

### 可选依赖

- `archiver`: 用于目录压缩功能

如果使用压缩功能，需要安装archiver：

```bash
npm install archiver
```

## 示例场景

### 场景1：整理照片目录

```bash
# 将大量照片按500MB限制拆分到多个目录
verteiler /Users/username/Photos \
  --max-size 500MB \
  --extensions .jpg,.jpeg,.png,.raw,.rw2 \
  --prefix photos \
  --compress-dirs
```

### 场景2：备份文件管理

```bash
# 将备份文件按1GB限制拆分，不压缩
verteiler /backup/data \
  --max-size 1GB \
  --extensions .tar,.gz,.zip \
  --prefix backup
```

### 场景3：编程式批量处理

```javascript
const { FileSplitter } = require('@nijiiro/verteiler');

async function processMultipleDirectories() {
  const directories = ['/path/to/dir1', '/path/to/dir2', '/path/to/dir3'];
  
  for (const dir of directories) {
    const splitter = new FileSplitter({
      MAX_DIR_SIZE: 2 * 1024 * 1024 * 1024, // 2GB
      TARGET_EXTENSIONS: ['.mp4', '.mov', '.avi'],
      DIR_PREFIX: 'video'
    });
    
    try {
      const result = await splitter.splitFiles(dir);
      console.log(`处理完成: ${dir}, 创建 ${result.directoriesCreated} 个目录`);
    } catch (error) {
      console.error(`处理失败: ${dir}, 错误: ${error.message}`);
    }
  }
}
```

## 故障排除

### 常见问题

1. **archiver库未安装**
   ```
   错误: 目录压缩功能需要安装archiver库
   解决: npm install archiver
   ```

2. **权限不足**
   ```
   错误: EACCES: permission denied
   解决: 确保对目标目录有读写权限
   ```

3. **文件过大**
   ```
   警告: 文件过大，跳过
   解决: 调整MAX_DIR_SIZE配置或手动处理大文件
   ```

### 日志文件

工具会在源目录中生成日志文件，格式为 `verteiler-{timestamp}.log`，包含详细的操作记录。

## 开发

### 项目结构

```
verteiler/
├── package.json          # npm包配置
├── src/                  # 源码目录 (TypeScript)
│   ├── index.ts         # 库入口点
│   ├── core/            # 核心业务逻辑
│   │   ├── FileSplitter.ts  # 文件拆分器类
│   │   ├── config.ts    # 配置和常量
│   │   └── utils.ts     # 工具函数
│   └── cli/             # CLI相关代码
│       └── index.ts     # CLI入口点
├── dist/                # 打包后的文件
│   ├── verteiler.js     # 库文件
│   ├── verteiler.d.ts   # 类型定义文件
│   ├── cli.js          # CLI文件
│   └── core/           # 类型定义文件
├── tsconfig.json        # TypeScript配置
├── bin/
│   └── verteiler        # CLI启动脚本
├── scripts/
│   └── publish.js       # 发布脚本
├── .husky/              # Git钩子配置
│   ├── commit-msg       # 提交信息验证
│   └── pre-commit       # 提交前构建检查
├── README.md            # 项目文档
├── CHANGELOG.md         # 自动生成的更新日志
├── CHANGELOG_GUIDE.md   # Changelog使用指南
├── PUBLISH.md           # 发布指南
├── LICENSE              # MIT许可证
├── rollup.config.js     # Rollup配置
├── commitlint.config.js # 提交信息验证配置
├── .versionrc.json      # 版本管理配置
├── .npmrc              # NPM配置
└── .gitignore          # Git忽略文件
```

### 本地开发

```bash
# 克隆项目
git clone <repository-url>
cd verteiler

# 安装依赖
pnpm install

# 构建项目
pnpm run build

# 链接到全局（用于测试）
npm link

# 测试CLI
verteiler --help

# 测试API
node -e "const { FileSplitter } = require('./dist/verteiler.js'); console.log('API测试成功');"

# 更新版本（预览）
pnpm run version:dry
```

### 发布到NPM

项目包含完整的发布脚本，支持自动版本管理和发布：

```bash
# 查看发布帮助
pnpm run publish:help

# 干运行（推荐先执行）
pnpm run publish:dry

# 发布补丁版本 (1.0.0 → 1.0.1)
pnpm run publish:patch

# 发布次版本 (1.0.0 → 1.1.0)
pnpm run publish:minor

# 发布主版本 (1.0.0 → 2.0.0)
pnpm run publish:major
```

详细的发布指南请参考 [PUBLISH.md](./PUBLISH.md)。

### Changelog管理

项目使用 `standard-version` 自动化工具管理版本和更新日志：

```bash
# 预览版本更新和changelog生成（推荐先执行）
pnpm run version:dry

# 自动版本更新和changelog生成
pnpm run version:patch  # 补丁版本
pnpm run version:minor  # 次版本
pnpm run version:major  # 主版本
```

详细的changelog使用指南请参考 [CHANGELOG_GUIDE.md](./CHANGELOG_GUIDE.md)。

## 许可证

MIT License

## 贡献

欢迎提交Issue和Pull Request！

## 更新日志

本项目使用 [Conventional Commits](https://www.conventionalcommits.org/zh-hans/v1.0.0/) 规范和自动化工具管理版本和更新日志。

### 查看更新日志

- **完整更新日志**: 查看 [CHANGELOG.md](./CHANGELOG.md)
- **使用指南**: 查看 [CHANGELOG_GUIDE.md](./CHANGELOG_GUIDE.md)

### 版本管理

项目使用 `standard-version` 自动管理版本和生成changelog：

```bash
# 预览版本更新（推荐先执行）
pnpm run version:dry

# 更新版本并生成changelog
pnpm run version:patch  # 补丁版本 (0.1.0 → 0.1.1)
pnpm run version:minor  # 次版本 (0.1.0 → 0.2.0)
pnpm run version:major  # 主版本 (0.1.0 → 1.0.0)
```

### 提交规范

项目要求使用规范的提交信息格式：

```bash
# 新功能
git commit -m "feat: 添加新功能"

# 修复bug
git commit -m "fix: 修复某个问题"

# 文档更新
git commit -m "docs: 更新文档"
```