Initial commit: Add cutPic, dexcel, and zip tools
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/target
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "zip"
|
||||
version = "0.1.1"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
zip = "2.2"
|
||||
walkdir = "2.5"
|
||||
arboard = "3.4"
|
||||
clipboard-win = "5.4"
|
||||
winapi = { version = "0.3", features = ["winuser"] }
|
||||
+422
@@ -0,0 +1,422 @@
|
||||
# zip - ZIP 打包工具 📦
|
||||
|
||||
一个快速、易用的命令行 ZIP 打包工具,支持将文件或文件夹打包成 zip 压缩文件。
|
||||
|
||||
## ✨ 功能特性
|
||||
|
||||
- 📁 **灵活输入**: 支持单个文件、多个文件或整个文件夹
|
||||
- 🗜️ **高效压缩**: 使用 Deflate 压缩算法
|
||||
- 📋 **剪贴板集成**: 可选择将生成的 zip 文件复制到剪贴板(Windows)
|
||||
- 🎯 **智能命名**: 自动生成有意义的文件名
|
||||
- 📊 **详细反馈**: 显示打包进度和统计信息
|
||||
- ⚡ **高性能**: 基于 Rust 开发,速度快,资源占用低
|
||||
- 🔒 **跨平台**: 支持 Windows、macOS、Linux
|
||||
|
||||
## 📦 安装
|
||||
|
||||
### 从源码编译
|
||||
|
||||
```bash
|
||||
cd zip
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
编译后的可执行文件位于 `target/release/zip.exe` (Windows) 或 `target/release/zip` (Unix)
|
||||
|
||||
### 添加到系统路径(可选)
|
||||
|
||||
将可执行文件复制到系统 PATH 目录,或将其所在目录添加到 PATH 环境变量中。
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 基本用法
|
||||
|
||||
```bash
|
||||
# 打包单个文件
|
||||
zip myfile.txt
|
||||
|
||||
# 打包文件夹
|
||||
zip myfolder
|
||||
|
||||
# 打包多个文件
|
||||
zip file1.txt file2.txt file3.txt
|
||||
```
|
||||
|
||||
### 指定输出文件名
|
||||
|
||||
```bash
|
||||
# 自定义输出的 zip 文件名
|
||||
zip myfile.txt -n archive.zip
|
||||
|
||||
# 打包文件夹并指定名称
|
||||
zip myfolder -n backup.zip
|
||||
```
|
||||
|
||||
### 复制到剪贴板
|
||||
|
||||
```bash
|
||||
# 打包后自动复制文件到剪贴板(Windows)
|
||||
zip myfile.txt -v
|
||||
|
||||
# 打包文件夹并复制到剪贴板
|
||||
zip myfolder -v -n backup.zip
|
||||
```
|
||||
|
||||
## 📖 命令参数
|
||||
|
||||
```bash
|
||||
zip [OPTIONS] <PATH...>
|
||||
|
||||
参数:
|
||||
<PATH...> 📁 要打包的文件或文件夹路径(可以指定多个,必需)
|
||||
-n, --name <FILE> 📝 输出的 zip 文件名(默认自动生成)
|
||||
-v, --clipboard 📋 复制 zip 文件路径到剪贴板
|
||||
-h, --help 显示帮助信息
|
||||
-V, --version 显示版本信息
|
||||
```
|
||||
|
||||
## 💡 使用示例
|
||||
|
||||
### 示例 1: 打包单个文件
|
||||
|
||||
```bash
|
||||
zip document.pdf
|
||||
```
|
||||
|
||||
**输出:**
|
||||
- 生成 `document.zip`
|
||||
- 位置: 与源文件相同的目录
|
||||
|
||||
### 示例 2: 打包整个文件夹
|
||||
|
||||
```bash
|
||||
zip project-folder
|
||||
```
|
||||
|
||||
**输出:**
|
||||
- 生成 `project-folder.zip`
|
||||
- 包含文件夹内的所有文件和子文件夹
|
||||
- 保留目录结构
|
||||
|
||||
### 示例 3: 打包多个文件
|
||||
|
||||
```bash
|
||||
zip readme.md config.json main.py
|
||||
```
|
||||
|
||||
**输出:**
|
||||
- 生成 `readme.zip`(以第一个文件命名)
|
||||
- 包含所有指定的文件
|
||||
|
||||
### 示例 4: 自定义输出文件名
|
||||
|
||||
```bash
|
||||
zip important-docs -n docs-backup-2024.zip
|
||||
```
|
||||
|
||||
**输出:**
|
||||
- 生成 `docs-backup-2024.zip`
|
||||
- 在当前目录创建
|
||||
|
||||
### 示例 5: 打包并复制到剪贴板(Windows)
|
||||
|
||||
```bash
|
||||
zip my-project -v -n project-archive.zip
|
||||
```
|
||||
|
||||
**输出:**
|
||||
- 生成 `project-archive.zip`
|
||||
- 文件路径自动复制到剪贴板
|
||||
- 可以直接在文件资源管理器中粘贴
|
||||
|
||||
### 示例 6: 批量备份多个文件夹
|
||||
|
||||
```bash
|
||||
zip folder1 folder2 folder3 -n multi-backup.zip
|
||||
```
|
||||
|
||||
**输出:**
|
||||
- 生成 `multi-backup.zip`
|
||||
- 包含所有三个文件夹的内容
|
||||
|
||||
## 📊 输出说明
|
||||
|
||||
### 控制台输出示例
|
||||
|
||||
```
|
||||
📦 开始打包...
|
||||
📂 源文件/文件夹: 3 个
|
||||
1. "file1.txt"
|
||||
2. "file2.txt"
|
||||
3. "myfolder"
|
||||
📄 输出文件: "archive.zip"
|
||||
|
||||
✓ file1.txt
|
||||
✓ file2.txt
|
||||
✓ myfolder/subfolder/file3.txt
|
||||
✓ myfolder/image.png
|
||||
✅ 打包完成!
|
||||
📊 共打包 4 个文件
|
||||
💾 文件大小: 1.25 MB
|
||||
📍 保存位置: C:\Users\username\Documents\archive.zip
|
||||
📋 已复制文件到剪切板: C:\Users\username\Documents\archive.zip
|
||||
```
|
||||
|
||||
### 文件命名规则
|
||||
|
||||
如果不指定 `-n` 参数:
|
||||
- **单个文件**: 使用文件的基本名 + `.zip`
|
||||
- `document.pdf` → `document.zip`
|
||||
- **多个文件**: 使用第一个文件的基本名 + `.zip`
|
||||
- `file1.txt file2.txt` → `file1.zip`
|
||||
- **文件夹**: 使用文件夹名 + `.zip`
|
||||
- `myfolder` → `myfolder.zip`
|
||||
|
||||
如果指定了 `-n` 参数:
|
||||
- 使用指定的完整路径和文件名
|
||||
|
||||
## 🎯 应用场景
|
||||
|
||||
### 1. 项目备份
|
||||
|
||||
```bash
|
||||
# 快速备份当前项目
|
||||
zip . -n project-backup-$(date +%Y%m%d).zip
|
||||
|
||||
# Windows PowerShell
|
||||
zip . -n "project-backup-$(Get-Date -Format 'yyyyMMdd').zip"
|
||||
```
|
||||
|
||||
### 2. 文件分享
|
||||
|
||||
```bash
|
||||
# 打包多个文档准备分享
|
||||
zip report.docx data.xlsx charts.png -n weekly-report.zip
|
||||
|
||||
# 复制路径到剪贴板,方便通过聊天软件发送
|
||||
zip weekly-report.zip -v
|
||||
```
|
||||
|
||||
### 3. 代码归档
|
||||
|
||||
```bash
|
||||
# 打包源代码(排除 node_modules 等需要在 .gitignore 中配置)
|
||||
zip src tests package.json -n source-code.zip
|
||||
```
|
||||
|
||||
### 4. 日志收集
|
||||
|
||||
```bash
|
||||
# 打包日志文件用于问题排查
|
||||
zip logs/ -n debug-logs.zip
|
||||
```
|
||||
|
||||
### 5. 资源打包
|
||||
|
||||
```bash
|
||||
# 打包游戏或应用资源
|
||||
zip assets/ configs/ -n game-resources.zip
|
||||
```
|
||||
|
||||
## 📁 目录结构处理
|
||||
|
||||
### 打包文件夹时的结构保留
|
||||
|
||||
假设你有以下目录结构:
|
||||
|
||||
```
|
||||
myproject/
|
||||
├── src/
|
||||
│ ├── main.rs
|
||||
│ └── utils.rs
|
||||
├── Cargo.toml
|
||||
└── README.md
|
||||
```
|
||||
|
||||
执行 `zip myproject` 后,zip 文件内部结构为:
|
||||
|
||||
```
|
||||
myproject/
|
||||
├── src/
|
||||
│ ├── main.rs
|
||||
│ └── utils.rs
|
||||
├── Cargo.toml
|
||||
└── README.md
|
||||
```
|
||||
|
||||
解压时会完整保留目录结构。
|
||||
|
||||
### 打包多个独立文件
|
||||
|
||||
执行 `zip file1.txt file2.txt` 后,zip 文件内部:
|
||||
|
||||
```
|
||||
file1.txt
|
||||
file2.txt
|
||||
```
|
||||
|
||||
文件直接放在根目录,不包含路径前缀。
|
||||
|
||||
## ⚙️ 技术细节
|
||||
|
||||
### 压缩算法
|
||||
|
||||
- **方法**: Deflate
|
||||
- **压缩级别**: 默认级别(平衡速度和压缩率)
|
||||
- **权限保留**: Unix 系统保留文件权限(0o755)
|
||||
|
||||
### 依赖库
|
||||
|
||||
- **clap**: 命令行参数解析
|
||||
- **zip**: ZIP 文件读写
|
||||
- **walkdir**: 递归遍历目录
|
||||
- **clipboard-win**: Windows 剪贴板操作(仅 Windows)
|
||||
- **arboard**: 跨平台剪贴板操作(非 Windows)
|
||||
- **winapi**: Windows API 调用(仅 Windows)
|
||||
|
||||
### 性能特点
|
||||
|
||||
- **流式写入**: 边读取边压缩,内存占用低
|
||||
- **并行处理**: 文件遍历使用 walkdir 优化
|
||||
- **增量打包**: 逐个添加文件,支持大文件
|
||||
|
||||
### 平台差异
|
||||
|
||||
#### Windows
|
||||
- 使用 `clipboard-win` 和 `winapi` 实现文件复制到剪贴板
|
||||
- 支持直接复制文件对象(可在资源管理器中粘贴)
|
||||
- 自动重试机制处理剪贴板占用问题
|
||||
|
||||
#### macOS / Linux
|
||||
- 使用 `arboard` 实现文本复制到剪贴板
|
||||
- 复制的是文件路径字符串
|
||||
- 需要手动在文件管理器中打开路径
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **隐藏文件**: 默认会打包隐藏文件(以 `.` 开头的文件)
|
||||
2. **符号链接**: 符号链接会被跟随,打包实际文件
|
||||
3. **空目录**: 空目录可能不会被包含在 zip 中
|
||||
4. **文件锁定**: 确保要打包的文件没有被其他程序独占锁定
|
||||
5. **磁盘空间**: 确保有足够的磁盘空间存放生成的 zip 文件
|
||||
6. **特殊字符**: 文件名中的特殊字符会被保留,但某些系统可能不兼容
|
||||
|
||||
## 🔧 常见问题
|
||||
|
||||
### Q1: 如何排除某些文件或文件夹?
|
||||
|
||||
目前版本不支持排除功能。建议:
|
||||
- 先将要打包的文件复制到临时目录
|
||||
- 或使用其他工具如 7-Zip、WinRAR
|
||||
|
||||
未来版本可能会添加 `--exclude` 参数。
|
||||
|
||||
### Q2: 压缩包太大怎么办?
|
||||
|
||||
- 检查是否包含了不必要的大文件(如视频、数据库)
|
||||
- 考虑先压缩大文件再打包
|
||||
- 使用专业的压缩工具进行二次压缩
|
||||
|
||||
### Q3: 如何在 Linux/macOS 上使用剪贴板功能?
|
||||
|
||||
在非 Windows 系统上,`-v` 参数会复制文件路径文本。你可以:
|
||||
- 在终端中使用 `xdg-open` (Linux) 或 `open` (macOS) 打开路径
|
||||
- 或在文件管理器中使用"前往文件夹"功能粘贴路径
|
||||
|
||||
### Q4: 打包速度慢?
|
||||
|
||||
- 检查是否有大量小文件(会增加 overhead)
|
||||
- 确认磁盘读写速度
|
||||
- 考虑使用 SSD 而非 HDD
|
||||
|
||||
### Q5: 如何处理中文文件名?
|
||||
|
||||
工具完全支持 UTF-8 编码的中文文件名,在 Windows、macOS、Linux 上都能正常工作。
|
||||
|
||||
## 📝 完整工作流示例
|
||||
|
||||
### 工作流 1: 日常备份脚本
|
||||
|
||||
创建一个批处理文件 `backup.bat` (Windows):
|
||||
|
||||
```batch
|
||||
@echo off
|
||||
set TIMESTAMP=%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%
|
||||
set TIMESTAMP=%TIMESTAMP: =0%
|
||||
|
||||
echo 正在备份项目...
|
||||
zip my-project -n "backup-%TIMESTAMP%.zip" -v
|
||||
|
||||
if %errorlevel% equ 0 (
|
||||
echo ✅ 备份成功!
|
||||
) else (
|
||||
echo ❌ 备份失败!
|
||||
)
|
||||
```
|
||||
|
||||
### 工作流 2: 项目发布准备
|
||||
|
||||
```bash
|
||||
# 1. 清理构建产物
|
||||
cargo clean
|
||||
|
||||
# 2. 打包源代码
|
||||
zip src/ Cargo.toml README.md LICENSE -n myproject-source.zip
|
||||
|
||||
# 3. 构建发布版本
|
||||
cargo build --release
|
||||
|
||||
# 4. 打包可执行文件
|
||||
zip target/release/myapp -n myproject-v1.0.0.zip
|
||||
|
||||
# 5. 复制路径到剪贴板
|
||||
zip myproject-v1.0.0.zip -v
|
||||
```
|
||||
|
||||
### 工作流 3: 日志收集和问题报告
|
||||
|
||||
```bash
|
||||
# 1. 创建临时目录
|
||||
mkdir temp-logs
|
||||
|
||||
# 2. 复制相关日志文件
|
||||
cp logs/*.log temp-logs/
|
||||
cp config.yaml temp-logs/
|
||||
|
||||
# 3. 打包
|
||||
zip temp-logs -n bug-report-logs.zip
|
||||
|
||||
# 4. 清理临时目录
|
||||
rm -rf temp-logs
|
||||
|
||||
# 5. 复制到剪贴板方便上传
|
||||
zip bug-report-logs.zip -v
|
||||
```
|
||||
|
||||
## 🤝 贡献
|
||||
|
||||
欢迎提交 Issue 和 Pull Request!
|
||||
|
||||
可能的改进方向:
|
||||
- 添加 `--exclude` 参数支持排除文件
|
||||
- 支持压缩级别选择
|
||||
- 添加密码保护功能
|
||||
- 支持分卷压缩
|
||||
- 显示压缩进度条
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本项目采用 MIT 许可证。
|
||||
|
||||
## 🙏 致谢
|
||||
|
||||
感谢以下开源项目:
|
||||
- [clap](https://github.com/clap-rs/clap) - 优秀的命令行参数解析库
|
||||
- [zip](https://github.com/zip-rs/zip) - ZIP 文件格式支持
|
||||
- [walkdir](https://github.com/BurntSushi/walkdir) - 高效的目录遍历
|
||||
- [clipboard-win](https://github.com/DoumanAsh/clipboard-win) - Windows 剪贴板支持
|
||||
- [arboard](https://github.com/1Password/arboard) - 跨平台剪贴板支持
|
||||
|
||||
---
|
||||
|
||||
**Made with ❤️ using Rust**
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
use clap::Parser;
|
||||
use std::path::PathBuf;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use walkdir::WalkDir;
|
||||
use zip::write::SimpleFileOptions;
|
||||
|
||||
/// ZIP 打包工具 - 将文件或文件夹打包成 zip 格式
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "zip")]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[command(
|
||||
about = "📦 ZIP 打包工具 - 将文件或文件夹快速打包成 zip 格式",
|
||||
long_about = "📦 ZIP 打包工具\n\n将指定的文件或文件夹打包成 zip 压缩文件。\n支持单个文件、多个文件或整个文件夹的打包。\n\n示例:\n zip src\n zip file1.txt file2.txt -n archive.zip\n zip myfolder -v -n backup.zip"
|
||||
)]
|
||||
struct Args {
|
||||
/// 📁 要打包的文件或文件夹路径(可以指定多个)
|
||||
#[arg(required = true, value_name = "PATH")]
|
||||
sources: Vec<PathBuf>,
|
||||
|
||||
/// 📝 输出的 zip 文件名(默认自动生成)
|
||||
#[arg(short = 'n', long = "name", value_name = "FILE")]
|
||||
name: Option<PathBuf>,
|
||||
|
||||
/// 📋 复制 zip 文件路径到剪切板
|
||||
#[arg(short = 'v', long = "clipboard")]
|
||||
clipboard: bool,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let args = Args::parse();
|
||||
|
||||
// 验证输入路径
|
||||
for source in &args.sources {
|
||||
if !source.exists() {
|
||||
eprintln!("❌ 错误: 路径不存在: {:?}", source);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 生成输出文件名
|
||||
let output_path = if let Some(name) = &args.name {
|
||||
name.clone()
|
||||
} else {
|
||||
// 根据第一个源文件/文件夹生成默认名称
|
||||
let first_source = &args.sources[0];
|
||||
let stem = first_source
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("archive");
|
||||
|
||||
let mut path = if args.sources.len() == 1 {
|
||||
first_source.parent().unwrap_or(std::path::Path::new(".")).to_path_buf()
|
||||
} else {
|
||||
std::env::current_dir()?
|
||||
};
|
||||
path.push(format!("{}.zip", stem));
|
||||
path
|
||||
};
|
||||
|
||||
println!("\n📦 开始打包...");
|
||||
println!("📂 源文件/文件夹: {} 个", args.sources.len());
|
||||
for (i, source) in args.sources.iter().enumerate() {
|
||||
println!(" {}. {:?}", i + 1, source);
|
||||
}
|
||||
println!("📄 输出文件: {:?}\n", output_path);
|
||||
|
||||
// 创建 zip 文件
|
||||
let zip_file = File::create(&output_path)?;
|
||||
let mut zip_writer = zip::ZipWriter::new(zip_file);
|
||||
|
||||
let options = SimpleFileOptions::default()
|
||||
.compression_method(zip::CompressionMethod::Deflated)
|
||||
.unix_permissions(0o755);
|
||||
|
||||
let mut file_count = 0;
|
||||
let mut total_size = 0u64;
|
||||
|
||||
// 遍历所有源路径
|
||||
for source in &args.sources {
|
||||
if source.is_file() {
|
||||
// 处理单个文件
|
||||
add_file_to_zip(&mut zip_writer, source, "", options, &mut file_count, &mut total_size)?;
|
||||
} else if source.is_dir() {
|
||||
// 处理文件夹
|
||||
add_dir_to_zip(&mut zip_writer, source, options, &mut file_count, &mut total_size)?;
|
||||
}
|
||||
}
|
||||
|
||||
zip_writer.finish()?;
|
||||
|
||||
println!("✅ 打包完成!");
|
||||
println!("📊 共打包 {} 个文件", file_count);
|
||||
println!("💾 文件大小: {}", format_bytes(total_size));
|
||||
println!("📍 保存位置: {:?}", output_path.canonicalize()?);
|
||||
|
||||
// 如果需要复制到剪切板
|
||||
if args.clipboard {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// 在 Windows 上复制文件到剪切板
|
||||
use clipboard_win::{formats::FileList, Setter};
|
||||
use winapi::um::winuser::{OpenClipboard, CloseClipboard, EmptyClipboard};
|
||||
use winapi::shared::windef::HWND;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
// 获取绝对路径(不带 \\?\ 前缀)
|
||||
let abs_path = if output_path.is_absolute() {
|
||||
output_path.clone()
|
||||
} else {
|
||||
std::env::current_dir()?.join(&output_path)
|
||||
};
|
||||
|
||||
// 转换为标准字符串格式
|
||||
let path_str = abs_path.to_string_lossy().to_string();
|
||||
let paths: Vec<&str> = vec![path_str.as_str()];
|
||||
|
||||
// 尝试多次打开剪贴板并复制文件
|
||||
for attempt in 0..5 {
|
||||
unsafe {
|
||||
// 尝试打开剪贴板
|
||||
if OpenClipboard(std::ptr::null_mut() as HWND) != 0 {
|
||||
// 清空剪贴板
|
||||
EmptyClipboard();
|
||||
|
||||
// 尝试写入文件列表
|
||||
match FileList.write_clipboard(&paths) {
|
||||
Ok(_) => {
|
||||
println!("📋 已复制文件到剪切板: {}", path_str);
|
||||
CloseClipboard();
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
CloseClipboard();
|
||||
if attempt < 4 {
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
} else {
|
||||
eprintln!("⚠️ 复制文件失败: {}", e);
|
||||
eprintln!("💡 提示: 请手动复制文件或使用资源管理器打开该位置");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if attempt < 4 {
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
} else {
|
||||
eprintln!("⚠️ 无法打开剪贴板,可能被其他程序占用");
|
||||
eprintln!("💡 提示: 请关闭其他使用剪贴板的程序后重试");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let canonical_path = output_path.canonicalize()?;
|
||||
let path_str = canonical_path.to_string_lossy().to_string();
|
||||
use arboard::Clipboard;
|
||||
match Clipboard::new() {
|
||||
Ok(mut clipboard) => {
|
||||
match clipboard.set_text(path_str.clone()) {
|
||||
Ok(_) => println!("📋 已复制路径到剪切板: {}", path_str),
|
||||
Err(e) => eprintln!("⚠️ 复制到剪切板失败: {}", e),
|
||||
}
|
||||
}
|
||||
Err(e) => eprintln!("⚠️ 无法访问剪切板: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 将单个文件添加到 zip
|
||||
fn add_file_to_zip(
|
||||
writer: &mut zip::ZipWriter<File>,
|
||||
file_path: &std::path::Path,
|
||||
base_path: &str,
|
||||
options: SimpleFileOptions,
|
||||
file_count: &mut usize,
|
||||
total_size: &mut u64,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let file_name = if base_path.is_empty() {
|
||||
file_path.file_name().unwrap().to_string_lossy().to_string()
|
||||
} else {
|
||||
format!("{}/{}", base_path, file_path.file_name().unwrap().to_string_lossy())
|
||||
};
|
||||
|
||||
writer.start_file(file_name, options)?;
|
||||
|
||||
let mut file = File::open(file_path)?;
|
||||
let file_size = file.metadata()?.len();
|
||||
*total_size += file_size;
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
file.read_to_end(&mut buffer)?;
|
||||
writer.write_all(&buffer)?;
|
||||
|
||||
*file_count += 1;
|
||||
println!(" ✓ {}", file_path.display());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 将整个文件夹添加到 zip
|
||||
fn add_dir_to_zip(
|
||||
writer: &mut zip::ZipWriter<File>,
|
||||
dir_path: &std::path::Path,
|
||||
options: SimpleFileOptions,
|
||||
file_count: &mut usize,
|
||||
total_size: &mut u64,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
for entry in WalkDir::new(dir_path).into_iter().filter_map(|e| e.ok()) {
|
||||
let path = entry.path();
|
||||
|
||||
if path.is_file() {
|
||||
// 计算相对路径
|
||||
let relative_path = path.strip_prefix(dir_path.parent().unwrap_or(dir_path))?;
|
||||
let file_name = relative_path.to_string_lossy().replace('\\', "/");
|
||||
|
||||
writer.start_file(file_name.clone(), options)?;
|
||||
|
||||
let mut file = File::open(path)?;
|
||||
let file_size = file.metadata()?.len();
|
||||
*total_size += file_size;
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
file.read_to_end(&mut buffer)?;
|
||||
writer.write_all(&buffer)?;
|
||||
|
||||
*file_count += 1;
|
||||
println!(" ✓ {}", path.display());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 格式化字节大小
|
||||
fn format_bytes(bytes: u64) -> String {
|
||||
const KB: u64 = 1024;
|
||||
const MB: u64 = KB * 1024;
|
||||
const GB: u64 = MB * 1024;
|
||||
|
||||
if bytes >= GB {
|
||||
format!("{:.2} GB", bytes as f64 / GB as f64)
|
||||
} else if bytes >= MB {
|
||||
format!("{:.2} MB", bytes as f64 / MB as f64)
|
||||
} else if bytes >= KB {
|
||||
format!("{:.2} KB", bytes as f64 / KB as f64)
|
||||
} else {
|
||||
format!("{} B", bytes)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user