nirajnale/file-packer-unpacker-java
GitHub: nirajnale/file-packer-unpacker-java
基于 Java 流式 I/O 和自定义二进制头部的命令行文件打包与解包工具,采用增量方式实现,用于学习和演示归档格式的基本原理。
Stars: 0 | Forks: 0
# 文件打包与解包工具 — Java
这是一款命令行工具,能够将目录中的多个文件打包成单个归档文件,并将其还原至原始状态。该工具使用 Java 编写,基于核心文件 I/O 流,并采用自定义的二进制头部格式在归档文件中存储每个文件的元数据。
## 功能说明
**打包**过程会遍历源目录,收集所有 `.txt` 文件,并按顺序将它们写入单个打包归档文件中。在每个文件的数据之前,会写入一个固定长度为 100 字节的头部,其中包含文件名和文件大小。打包完成后,该工具会输出一份报告,显示扫描到的文件数和已打包的文件数。
```
-----------------------------------------------------
------- Marvellous Packer Unpacker CUI Module -------
-----------------------------------------------------
----------------- Packing Activity ------------------
Enter the name of Directory that you want to pack :
> reports
Enter the name of packed file to create :
> archive.pak
File packed with name : report_q1.txt
File packed with name : report_q2.txt
File packed with name : summary.txt
-----------------------------------------------------
Packing activity completed..
Number of files scanned : 5
Number of files packed : 3
-----------------------------------------------------
```
```
-----------------------------------------------------
------- Marvellous Packer Unpacker CUI Module -------
-----------------------------------------------------
---------------- Unpacking Activity -----------------
Enter the name of packed file to open :
> archive.pak
File dropped with name : report_q1.txt
File dropped with name : report_q2.txt
File dropped with name : summary.txt
-----------------------------------------------------
Unpacking activity completed..
Number of files unpacked : 3
-----------------------------------------------------
```
## 归档格式
该归档文件使用简单的顺序二进制格式。每个打包的文件都存储为固定大小的头部,紧接着是该文件的原始字节:
```
┌─────────────────────────────────────────────────────┐
│ HEADER (100 bytes, space-padded) │
│ Format: " " │
│ Example: "report_q1.txt 2048 " │
├─────────────────────────────────────────────────────┤
│ FILE DATA (filesize bytes) │
│ Raw bytes of the original file │
├─────────────────────────────────────────────────────┤
│ HEADER (100 bytes) — next file │
├─────────────────────────────────────────────────────┤
│ FILE DATA — next file │
└─────────────────────────────────────────────────────┘
```
固定的 100 字节头部长度使得解包器只需调用一次 `fiobj.read(Header, 0, 100)` 即可读取头部,然后使用 `String.trim()` 和 `split(" ")` 解析出文件名和大小,最后读取恰好 `filesize` 字节的数据。
## 关键实现细节
**打包器 (`program542.java`)**
- 在目标打包文件上打开 `FileOutputStream`
- 使用 `File.listFiles()` 遍历源目录
- 使用 `String.endsWith(".txt")` 过滤 `.txt` 文件
- 将每个头部构造为 `" "`,并用空格填充至恰好 100 字节
- 使用 `foobj.write(Header.getBytes(), 0, 100)` 写入头部
- 使用 1024 字节的缓冲区循环流式传输文件字节:
`while ((iRet = fiobj.read(Buffer)) != -1) foobj.write(Buffer, 0, iRet)`
- 跟踪并报告扫描的文件数与打包的文件数
**解包器 (`program541.java`)**
- 在打包归档文件上打开 `FileInputStream`
- 循环调用 `fiobj.read(Header, 0, 100)` —— 读取每个 100 字节的头部
- 去除空格并按空格分割,以提取文件名 (`Tokens[0]`) 和大小 (`Tokens[1]`)
- 使用 `File.createNewFile()` 创建输出文件
- 读取恰好 `FileSize` 字节的数据:`fiobj.read(Buffer, 0, FileSize)`
- 写入输出文件并关闭流
- 报告解包的文件总数
## 项目开发过程
该实现是增量开发的,每个版本都在前一个版本的基础上增加了一项功能:
| 阶段 | 添加的功能 |
|---|---|
| 文件创建 | `File.createNewFile()`, `FileOutputStream` 基础 |
| 文件读取 | `FileInputStream`, 缓冲读取 |
| 文件复制 | 源到目标的流式传输 |
| 目录遍历 | `File.listFiles()`, 文件名和大小提取 |
| 头部构造 | 用于元数据的固定 100 字节填充 |
| 打包 | 顺序写入头部 + 文件数据 |
| 解包 | 头部解析,文件重建,循环直到 EOF |
## 项目结构
```
file-packer-unpacker-java/
│
├── PackFront.java/ # Packer implementation (incremental versions)
│ ├── program501.java # Initial setup
│ ├── program502.java – ... # Incremental steps
│ └── program542.java # Final complete packer
│
├── UnpackFront.java/ # Unpacker implementation (incremental versions)
│ ├── program533.java # String parsing fundamentals
│ ├── program534.java – ... # Incremental steps
│ └── program541.java # Final complete unpacker
│
└── README.md
```
## 构建与运行
**环境要求:** JDK 8 或更高版本
**编译并运行打包器:**
```
cd "PackFront.java"
javac program542.java
java program542
```
**编译并运行解包器:**
```
cd "UnpackFront.java"
javac program541.java
java program541
```
**快速测试:**
```
# 创建一个包含一些文本文件的测试目录
mkdir test_dir
echo "Hello from file one" > test_dir/file1.txt
echo "Hello from file two" > test_dir/file2.txt
# 运行 packer — 出现提示时输入 "test_dir" 和 "archive.pak"
java program542
# 在不同的目录中运行 unpacker — 出现提示时输入 "archive.pak"
mkdir output_dir && cd output_dir
java -cp .. program541
# file1.txt 和 file2.txt 在此处被重新创建
```
## 设计决策
**为什么使用固定的 100 字节头部?**
固定长度的头部使得解包循环简单且可靠 —— 恰好读取 100 字节,进行解析,再恰好读取 `filesize` 字节,如此循环。变长头部则需要分隔符或长度前缀,这会增加复杂性。100 字节足以容纳任何合理的文件名和大小而不会发生截断。
**为什么只过滤 `.txt` 文件?**
当前的实现针对文本文件,以将范围集中在演示核心打包机制上。该过滤器只是一个简单的 `endsWith()` 检查,可以轻松移除或扩展以支持任何文件类型。
**为什么每个开发步骤都要使用单独的文件?**
这种增量式的文件结构保留了学习过程 —— 每个版本都只增加一项功能。这使得我们可以轻松追溯最终实现的每一部分是如何开发和测试的。
## 未来改进
## 许可证
Niraj Vijaysinh Nale
标签:JS文件枚举, 二进制处理, 域名枚举, 归档工具, 文件IO, 文件打包