Compare commits
29 Commits
1.2-SNAPSH
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e7a1bfbee | |||
| bc5bfe0d00 | |||
| 341ec1b0f6 | |||
| 9817a41801 | |||
| e3263fc56e | |||
| ec1728f4e2 | |||
| 7464c1fc9f | |||
| faa29bb664 | |||
| a0d6c6570f | |||
| 9d452ded62 | |||
| e31384a848 | |||
| c38b8857d5 | |||
| 1729ea3319 | |||
| 7bb822db9f | |||
| 3ec2e132c3 | |||
| c8462d5c68 | |||
| 441891652c | |||
| 03e223df9b | |||
| 0304fed324 | |||
| 6934ac8663 | |||
| 072d2a841b | |||
| 4fd86d332f | |||
| eadcca5b59 | |||
| e557dcde11 | |||
| 00b0636a0d | |||
| ac361cebcf | |||
| 9ebb6f8354 | |||
| d73881fd86 | |||
| 1e577622b3 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -42,4 +42,5 @@ build/
|
|||||||
/target
|
/target
|
||||||
/.idea
|
/.idea
|
||||||
/.mvn
|
/.mvn
|
||||||
/build_release
|
/build_release
|
||||||
|
/upload/
|
||||||
|
|||||||
20
INFO.md
Normal file
20
INFO.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
1.5-SNAPSHOT
|
||||||
|
|
||||||
|
此版本包含:
|
||||||
|
1. 压缩/解压 (`compress`)
|
||||||
|
2. 终端彩色输出 (`echo`)
|
||||||
|
2.1 预定义颜色打印 — `ColorPrintln`
|
||||||
|
2.2 自定义颜色打印 — `ColorHexPrintln`
|
||||||
|
2.3 Minecraft 风格多色打印 — `ColorPrintlnPlus`
|
||||||
|
3. 文件创建与保存 (`file/CreateFile`)
|
||||||
|
4. 文件加密/解密 (`file/EncryptionAndDecryption`)
|
||||||
|
5. 随机生成器 (`random`)
|
||||||
|
6. 硬件标识生成器 (`hwid`)
|
||||||
|
7. 版本信息 (`version`)
|
||||||
|
8. 运行时信息 (`runtime`)
|
||||||
|
6.1 获取运行时临时目录
|
||||||
|
6.2 新建临时目录
|
||||||
|
6.3 获取临时目录
|
||||||
|
6.4 获取当前运行的JAR文件路径
|
||||||
|
|
||||||
|
在 1.4-SNAPSHOT 的基础上调整了API接口,修复了一些BUG
|
||||||
364
README.md
364
README.md
@@ -1,38 +1,54 @@
|
|||||||
# GNU Java Utils Library
|
# GNU Java Utils Library
|
||||||
|
|
||||||
一个轻量级 Java 工具库,提供常用的压缩/解压与终端彩色输出功能等。
|
一个轻量级 Java 工具库,提供压缩/解压、终端彩色输出、文件创建与管理、文件加密/解密、随机数生成、硬件标识等功能。
|
||||||
|
|
||||||
|
> 注:此项目/仓库已同步到 Github:[https://github.com/databasemysql233-debug/gnu_java_utils_library.git](https://github.com/databasemysql233-debug/gnu_java_utils_library.git)
|
||||||
|
> 原项目/仓库链接:[https://git.opensource-studio.info/database-mysql/gnu_java_utils_library.git](https://git.opensource-studio.info/database-mysql/gnu_java_utils_library.git)
|
||||||
|
|
||||||
## 技术栈
|
## 技术栈
|
||||||
|
|
||||||
- **Java 25**
|
- **Java 25**
|
||||||
- **Maven** 构建
|
- **Maven** 构建
|
||||||
- **Apache Commons Compress 1.28.0**(压缩功能依赖)
|
- **Apache Commons Compress 1.28.0**(压缩功能依赖)
|
||||||
|
- **JUnit Jupiter 5.13.0**(测试框架)
|
||||||
|
|
||||||
## 项目结构
|
## 项目结构
|
||||||
|
|
||||||
```
|
```
|
||||||
src/main/java/top/gtb520/java/gnu/java/utils/library/
|
src/main/java/top/gtb520/java/gnu/java/utils/library/
|
||||||
├── main.java # 统一入口接口
|
├── main.java # 统一入口接口
|
||||||
├── utils/
|
├── version.java # 项目版本信息
|
||||||
│ ├── compress/
|
└── utils/
|
||||||
│ │ └── compress.java # 压缩/解压工具类
|
├── compress/
|
||||||
│ └── print/
|
│ └── compress.java # 压缩/解压工具类
|
||||||
│ ├── echo.java # 终端彩色输出工具类
|
├── file/
|
||||||
│ └── ColorNameEnum.java # 颜色名称枚举
|
│ ├── CreateFile.java # 文件创建/保存工具类
|
||||||
|
│ └── EncryptionAndDecryption.java # 文件加密/解密工具类
|
||||||
|
├── print/
|
||||||
|
│ ├── echo.java # 终端彩色输出工具类
|
||||||
|
│ └── ColorNameEnum.java # 颜色名称枚举
|
||||||
|
└── processing/
|
||||||
|
├── random/
|
||||||
|
│ └── defaults.java # 随机数/字符串生成器
|
||||||
|
├── hwid/
|
||||||
|
│ └── getHWID.java # 硬件标识生成器
|
||||||
|
└── runtime/
|
||||||
|
├── RunEnv.java # 运行环境信息
|
||||||
|
└── JavaRunTimeEnv.java # Java 运行时环境信息
|
||||||
```
|
```
|
||||||
|
|
||||||
## 功能模块
|
## 功能模块
|
||||||
|
|
||||||
### 1. 压缩/解压 (`compress`)
|
### 1. 压缩/解压 (`compress`)
|
||||||
|
|
||||||
支持 **Tar.Gz** 和 **Zip** 两种压缩格式的压缩与解压操作,支持单文件和整个目录的递归处理。
|
支持 **Tar.Gz** 和 **Zip** 两种格式的压缩与解压,支持单文件和目录递归处理。
|
||||||
|
|
||||||
| 方法 | 说明 | 参数 |
|
| 方法 | 说明 | 参数 |
|
||||||
|------------------------------------|---------------------|------------------------------|
|
|------------------------------------|--------------------------|-------------------------------------|
|
||||||
| `compress.MakeTarGz(source, dest)` | 将文件或目录压缩为 `.tar.gz` | `source` 源路径,`dest` 压缩包输出路径 |
|
| `compress.MakeTarGz(source, dest)` | 将文件或目录压缩为 `.tar.gz` | `source` 源路径,`dest` 压缩包输出路径 |
|
||||||
| `compress.UnTarGz(source, dest)` | 解压 `.tar.gz` 到指定目录 | `source` 压缩包路径,`dest` 解压目标目录 |
|
| `compress.UnTarGz(source, dest)` | 解压 `.tar.gz` 到指定目录 | `source` 压缩包路径,`dest` 解压目标目录 |
|
||||||
| `compress.MakeZip(source, dest)` | 将文件或目录压缩为 `.zip` | `source` 源路径,`dest` 压缩包输出路径 |
|
| `compress.MakeZip(source, dest)` | 将文件或目录压缩为 `.zip` | `source` 源路径,`dest` 压缩包输出路径 |
|
||||||
| `compress.UnZip(source, dest)` | 解压 `.zip` 到指定目录 | `source` 压缩包路径,`dest` 解压目标目录 |
|
| `compress.UnZip(source, dest)` | 解压 `.zip` 到指定目录 | `source` 压缩包路径,`dest` 解压目标目录 |
|
||||||
|
|
||||||
**使用示例:**
|
**使用示例:**
|
||||||
|
|
||||||
@@ -63,12 +79,15 @@ main.compress.TarGz(source, dest);
|
|||||||
|
|
||||||
### 2. 终端彩色输出 (`echo`)
|
### 2. 终端彩色输出 (`echo`)
|
||||||
|
|
||||||
基于 ANSI 转义序列,在终端中输出彩色文本。
|
基于 ANSI 转义序列,在终端中输出彩色文本。提供三种打印方式:
|
||||||
|
|
||||||
| 方法 | 说明 | 参数 |
|
#### 2.1 预定义颜色打印 — `ColorPrintln`
|
||||||
|-------------------------------------------|------------------|-------------------------------------|
|
|
||||||
| `echo.ColorPrintln(message, color)` | 使用预定义颜色打印 | `message` 文本内容,`color` 颜色枚举 |
|
使用颜色枚举快速输出。
|
||||||
| `echo.ColorHexPrintln(message, hexColor)` | 使用自定义 ANSI 颜色码打印 | `message` 文本内容,`hexColor` ANSI 颜色代码 |
|
|
||||||
|
| 方法 | 说明 | 参数 |
|
||||||
|
|-------------------------------------|-------------|--------------------------------|
|
||||||
|
| `echo.ColorPrintln(message, color)` | 使用预定义颜色打印 | `message` 文本,`color` 颜色枚举 |
|
||||||
|
|
||||||
**支持的颜色枚举 (`ColorNameEnum`):**
|
**支持的颜色枚举 (`ColorNameEnum`):**
|
||||||
|
|
||||||
@@ -81,85 +100,282 @@ main.compress.TarGz(source, dest);
|
|||||||
| `PURPLE` | 紫色 |
|
| `PURPLE` | 紫色 |
|
||||||
| `CYAN` | 青色 |
|
| `CYAN` | 青色 |
|
||||||
|
|
||||||
**使用示例:**
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import top.gtb520.java.gnu.java.utils.library.utils.print.echo;
|
import top.gtb520.java.gnu.java.utils.library.utils.print.echo;
|
||||||
import top.gtb520.java.gnu.java.utils.library.utils.print.ColorNameEnum;
|
import top.gtb520.java.gnu.java.utils.library.utils.print.ColorNameEnum;
|
||||||
|
|
||||||
// 使用预定义颜色
|
|
||||||
echo.ColorPrintln("错误信息", ColorNameEnum.RED);
|
echo.ColorPrintln("错误信息", ColorNameEnum.RED);
|
||||||
echo.ColorPrintln("成功信息", ColorNameEnum.GREEN);
|
echo.ColorPrintln("成功信息", ColorNameEnum.GREEN);
|
||||||
|
```
|
||||||
|
|
||||||
// 使用自定义 ANSI 颜色码
|
#### 2.2 自定义颜色打印 — `ColorHexPrintln`
|
||||||
echo.ColorHexPrintln("自定义橙色", "38;5;208");
|
|
||||||
|
支持 `#RRGGBB`、`#RGB` 十六进制颜色及 ANSI 颜色代码。
|
||||||
|
|
||||||
|
| 方法 | 说明 | 参数 |
|
||||||
|
|-------------------------------------------|------------------|-------------------------------------|
|
||||||
|
| `echo.ColorHexPrintln(message, hexColor)` | 使用自定义颜色码打印 | `message` 文本,`hexColor` 颜色代码 |
|
||||||
|
|
||||||
|
**支持的颜色格式:**
|
||||||
|
|
||||||
|
| 格式 | 示例 | 说明 |
|
||||||
|
|-------------|-------------|------------------------|
|
||||||
|
| `#RRGGBB` | `#FFC0CB` | 十六进制真彩色,自动转 ANSI 序列 |
|
||||||
|
| `#RGB` | `#F0F` | 十六进制简写,自动扩展后转 ANSI 序列 |
|
||||||
|
| ANSI 颜色码 | `31` | 标准 ANSI 颜色(如 31=红色) |
|
||||||
|
| ANSI 扩展色 | `38;5;208` | 256 色扩展(如 208=橙色) |
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 十六进制颜色
|
||||||
|
echo.ColorHexPrintln("粉色文本", "#FFC0CB");
|
||||||
|
echo.ColorHexPrintln("简写青色", "#0FF");
|
||||||
|
|
||||||
|
// ANSI 颜色码
|
||||||
|
echo.ColorHexPrintln("红色文本", "31");
|
||||||
|
echo.ColorHexPrintln("扩展橙色", "38;5;208");
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.3 Minecraft 风格多色打印 — `ColorPrintlnPlus`
|
||||||
|
|
||||||
|
在单行内使用 `&X` 格式码打印多种颜色和文本效果,仿照 Minecraft 格式化语法。
|
||||||
|
|
||||||
|
| 方法 | 说明 | 参数 |
|
||||||
|
|------------------------------------|-----------------------|-----------------------------|
|
||||||
|
| `echo.ColorPrintlnPlus(message)` | 解析 `&X` 格式码后打印多色文本 | `message` 含格式码的文本 |
|
||||||
|
|
||||||
|
**颜色代码:**
|
||||||
|
|
||||||
|
| 代码 | 颜色 | 代码 | 颜色 |
|
||||||
|
|------|--------|------|--------|
|
||||||
|
| `&0` | 黑色 | `&8` | 深灰色 |
|
||||||
|
| `&1` | 深蓝色 | `&9` | 浅蓝色 |
|
||||||
|
| `&2` | 深绿色 | `&a` | 浅绿色 |
|
||||||
|
| `&3` | 深青色 | `&b` | 浅青色 |
|
||||||
|
| `&4` | 深红色 | `&c` | 浅红色 |
|
||||||
|
| `&5` | 深紫色 | `&d` | 粉色 |
|
||||||
|
| `&6` | 橙色 | `&e` | 黄色 |
|
||||||
|
| `&7` | 灰色 | `&f` | 白色 |
|
||||||
|
|
||||||
|
**格式代码:**
|
||||||
|
|
||||||
|
| 代码 | 效果 | 代码 | 效果 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| `&l` | 加粗 | `&k` | 乱码(随机字符替换) |
|
||||||
|
| `&m` | 删除线 | `&r` | 重置 |
|
||||||
|
| `&n` | 下划线 | | |
|
||||||
|
| `&o` | 斜体 | | |
|
||||||
|
|
||||||
|
> 格式码大小写不敏感,`&A` 与 `&a` 等效。非合法格式码(如 `&z`)将原样保留。
|
||||||
|
|
||||||
|
```java
|
||||||
|
import top.gtb520.java.gnu.java.utils.library.utils.print.echo;
|
||||||
|
|
||||||
|
// 多色文本
|
||||||
|
echo.ColorPrintlnPlus("&6Welcome &2to &4Project");
|
||||||
|
|
||||||
|
// 混合格式效果
|
||||||
|
echo.ColorPrintlnPlus("&4&l粗体红色 &n&9下划线蓝色");
|
||||||
|
|
||||||
|
// 乱码效果(每次调用生成不同随机字符)
|
||||||
|
echo.ColorPrintlnPlus("&k这是一段乱码文字");
|
||||||
|
|
||||||
|
// 重置后恢复普通文本
|
||||||
|
echo.ColorPrintlnPlus("&4红色文字 &r普通文字");
|
||||||
```
|
```
|
||||||
|
|
||||||
也可以通过统一入口 `main` 接口调用:
|
也可以通过统一入口 `main` 接口调用:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import top.gtb520.java.gnu.java.utils.library.main;
|
import top.gtb520.java.gnu.java.utils.library.main;
|
||||||
import top.gtb520.java.gnu.java.utils.library.utils.print.ColorNameEnum;
|
|
||||||
|
|
||||||
main.echo.Println("警告信息", ColorNameEnum.YELLOW);
|
main.echo.Println("警告信息", ColorNameEnum.YELLOW);
|
||||||
main.echo.ColorHexPrintln("自定义颜色", "31");
|
main.echo.ColorHexPrintln("自定义颜色", "#FF0000");
|
||||||
|
main.echo.ColorPrintlnPlus("&a多色 &c文本");
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. 随机生成器 (`random`)
|
### 3. 文件创建与保存 (`file/CreateFile`)
|
||||||
|
|
||||||
随机生成器,提供生成随机字符串、数字、UUID 等功能。
|
支持创建普通/二进制文件,以及异步字节流文件保存。
|
||||||
|
|
||||||
| 方法 | 说明 | 参数 |
|
| 方法 | 说明 | 参数 |
|
||||||
|-------------------------------------------------------------|--------------|-----------------------------------|
|
|-------------------------------------------------------|---------------------------------------|-------------------------------------------------------------|
|
||||||
| `processing.random.defaults.random(start_scope, end_scope)` | 根据输入的范围生成随机数 | `start_scope` 起始数,`end_scope` 结束数 |
|
| `CreateFile.NewFile(FileName, isBin, binFileType)` | 创建空白文件,二进制文件自动写入文件头 | `FileName` 文件名,`isBin` 是否二进制,`binFileType` 二进制类型(普通文件传 `null`) |
|
||||||
| `processing.random.defaults.randomString(int length)` | 生成指定长度的随机字符串 | `length` 字符串长度 |
|
| `CreateFile.StreamSaveFile(file, TargePath, FileName)` | 以字节流方式异步保存文件到目标路径 | `file` 源文件,`TargePath` 目标目录,`FileName` 保存的文件名 |
|
||||||
|
|
||||||
**使用示例:**
|
**二进制文件类型 (`BinFileType`):**
|
||||||
|
|
||||||
|
| 枚举值 | 文件头标识 |
|
||||||
|
|-------|----------------|
|
||||||
|
| `ELF` | `7F 45 4C 46` |
|
||||||
|
| `PE` | `4D 5A` |
|
||||||
|
|
||||||
```java
|
```java
|
||||||
package top.gtb520.java.gnu.java.utils.library.utils.processing.random;
|
import top.gtb520.java.gnu.java.utils.library.utils.file.CreateFile;
|
||||||
|
import top.gtb520.java.gnu.java.utils.library.utils.file.CreateFile.BinFileType;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
defaults.random(1, 100);
|
// 创建普通空白文件
|
||||||
defaults.randomString(10);
|
File plainFile = CreateFile.NewFile("/path/to/file.txt", false, null);
|
||||||
```
|
|
||||||
|
|
||||||
### 4. HWID生成
|
// 创建 ELF 二进制文件(自动写入 ELF 文件头)
|
||||||
|
File elfFile = CreateFile.NewFile("/path/to/program", true, BinFileType.ELF);
|
||||||
|
|
||||||
HWID生成器,提供生成硬件ID的功能。
|
// 创建 PE 二进制文件(自动写入 MZ 文件头)
|
||||||
|
File peFile = CreateFile.NewFile("/path/to/app.exe", true, BinFileType.PE);
|
||||||
|
|
||||||
| 方法 | 说明 | 参数 |
|
// 异步字节流保存文件
|
||||||
|------------------------------------------|--------|----|
|
boolean success = CreateFile.StreamSaveFile(sourceFile, Path.of("/path/to/dest"), "output.bin");
|
||||||
| `processing.hwid.getHWID.generateHWID()` | 生成HWID | 无 |
|
```
|
||||||
|
|
||||||
**使用示例:**
|
### 4. 文件加密/解密 (`file/EncryptionAndDecryption`)
|
||||||
|
|
||||||
|
支持 **AES**、**DES** 对称加密,加密后覆盖原文件,密钥单独保存到指定目录。解密时从密钥文件读取密钥,解密后覆盖还原原文件。
|
||||||
|
|
||||||
|
| 方法 | 说明 | 参数 |
|
||||||
|
|----------------------------------------------------------------|--------------------------|-----------------------------------------------|
|
||||||
|
| `EncryptionAndDecryption.EncryptionFile(file, type, keyPath)` | 异步加密文件,覆盖原文件,密钥保存到 keyPath | `file` 文件,`type` 加密类型,`keyPath` 密钥目录路径 |
|
||||||
|
| `EncryptionAndDecryption.DecryptionFile(file, type, keyfile)` | 异步解密文件,覆盖还原原文件 | `file` 加密文件,`type` 加密类型,`keyfile` 密钥文件 |
|
||||||
|
|
||||||
|
**加密类型 (`EncryptionType`):**
|
||||||
|
|
||||||
|
| 枚举值 | 算法 | 密钥长度 |
|
||||||
|
|-------|---------------------|---------|
|
||||||
|
| `AES` | AES/ECB/PKCS5Padding | 128 bit |
|
||||||
|
| `DES` | DES/ECB/PKCS5Padding | 56 bit |
|
||||||
|
| `RSA` | AES(对称替代) | 128 bit |
|
||||||
|
|
||||||
|
> 加密后密钥文件命名为 `原文件名.key`,保存在 `keyPath` 目录下。
|
||||||
|
> 加密/解密均覆盖写回原文件,不添加额外后缀。
|
||||||
|
|
||||||
|
```java
|
||||||
|
import top.gtb520.java.gnu.java.utils.library.utils.file.EncryptionAndDecryption;
|
||||||
|
import top.gtb520.java.gnu.java.utils.library.utils.file.EncryptionAndDecryption.EncryptionType;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
|
||||||
|
File file = new File("/path/to/data.txt");
|
||||||
|
Path keyDir = Path.of("/path/to/keys");
|
||||||
|
|
||||||
|
// 加密文件(密钥保存到 keyDir/data.txt.key)
|
||||||
|
File encrypted = EncryptionAndDecryption.EncryptionFile(file, EncryptionType.AES, keyDir);
|
||||||
|
|
||||||
|
// 解密文件(从密钥文件读取密钥)
|
||||||
|
File keyFile = keyDir.resolve("data.txt.key").toFile();
|
||||||
|
File decrypted = EncryptionAndDecryption.DecryptionFile(file, EncryptionType.AES, keyFile);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 随机生成器 (`random`)
|
||||||
|
|
||||||
|
| 方法 | 说明 | 参数 |
|
||||||
|
|---------------------------------------------|--------------|------------------------------|
|
||||||
|
| `defaults.random(start_scope, end_scope)` | 生成指定范围内的随机数 | `start_scope` 起始,`end_scope` 结束 |
|
||||||
|
| `defaults.randomString(length)` | 生成指定长度的随机字符串 | `length` 字符串长度 |
|
||||||
|
|
||||||
|
```java
|
||||||
|
import top.gtb520.java.gnu.java.utils.library.utils.processing.random.defaults;
|
||||||
|
|
||||||
|
int num = defaults.random(1, 100); // 生成 1~100 的随机整数
|
||||||
|
String str = defaults.randomString(16); // 生成 16 位随机字符串(大小写字母+数字)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 硬件标识生成器 (`hwid`)
|
||||||
|
|
||||||
|
组合 CPU、主板、MAC 地址、硬盘序列号等信息,通过 SHA-256 生成唯一的硬件标识符(HWID)。支持 Windows、macOS、Linux 及 Android 平台。
|
||||||
|
|
||||||
|
| 方法 | 说明 | 参数 |
|
||||||
|
|------------------------|----------|----|
|
||||||
|
| `getHWID.generateHWID()` | 生成硬件标识符 | 无 |
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import top.gtb520.java.gnu.java.utils.library.utils.processing.hwid.getHWID;
|
import top.gtb520.java.gnu.java.utils.library.utils.processing.hwid.getHWID;
|
||||||
|
|
||||||
String Hwid_Str;
|
String hwid = getHWID.generateHWID();
|
||||||
Hwid_Str = getHWID.generateHWID();
|
System.out.println("HWID: " + hwid);
|
||||||
System.out.println("HWID: " + Hwid_Str);
|
```
|
||||||
System.out.println("HWID length: " + Hwid_Str.length());
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. 终端颜色打印Plus(ColorPrintlnPlus)
|
### 7. 版本信息 (`version`)
|
||||||
|
|
||||||
在单行内打印多种颜色的文字,包含Minecraft格式代码的文本
|
提供项目版本信息及编译时 Java/OpenJDK 版本常量。
|
||||||
|
|
||||||
| 方法 | 说明 | 参数 |
|
| 方法 | 说明 |
|
||||||
|------------------------------------|----------------------|-----------------------------|
|
|----------------------------|----------------|
|
||||||
| `ColorPrintlnPlus(String Message)` | 仿照Minecraft类型的文本终端打印 | `message` 待打印的文本,包含颜色与功能控制符 |
|
| `version.GetVersion()` | 获取项目版本号 |
|
||||||
|
| `version.GetJavaVersion()` | 获取编译时 Java 版本 |
|
||||||
**使用示例:**
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import top.gtb520.java.gnu.java.utils.library.utils.print.echo;
|
import top.gtb520.java.gnu.java.utils.library.version;
|
||||||
|
|
||||||
public class Main {
|
version v = new version();
|
||||||
public static void main() {
|
System.out.println("项目版本: " + v.GetVersion());
|
||||||
echo.ColorPrintlnPlus("&dHello &2World&6!");
|
System.out.println("Java版本: " + v.GetJavaVersion());
|
||||||
}
|
```
|
||||||
}
|
|
||||||
|
也可以通过统一入口调用:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import top.gtb520.java.gnu.java.utils.library.main;
|
||||||
|
|
||||||
|
main.version.getVersion(); // 项目版本
|
||||||
|
main.version.getJavaVersion(); // Java 版本
|
||||||
|
main.version.getOpenJDKVersion(); // OpenJDK 版本
|
||||||
|
main.version.getOpenJDKRuntimeEnvironment(); // OpenJDK 运行时环境
|
||||||
|
main.version.getOpenJDK64BitServerVM(); // OpenJDK 64-Bit Server VM
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. 运行时信息 (`runtime`)
|
||||||
|
|
||||||
|
获取当前 Java 运行时环境信息,包括 JAR 路径、JVM 版本、位数等。
|
||||||
|
|
||||||
|
**运行环境 (`RunEnv`):**
|
||||||
|
|
||||||
|
| 方法 | 说明 |
|
||||||
|
|-------------------------------|------------------------------------------|
|
||||||
|
| `RunEnv.getJarPath()` | 获取当前运行的 JAR 路径 |
|
||||||
|
| `RunEnv.getTempDir()` | 获取系统临时目录路径 |
|
||||||
|
| `RunEnv.newTempDir()` | 在系统临时目录下新建时间戳子目录,返回路径;已存在则返回 `null` |
|
||||||
|
| `RunEnv.newJarThisTempDir()` | 在 JAR 所在目录下新建时间戳子目录,返回路径;已存在则返回 `null` |
|
||||||
|
|
||||||
|
**Java 运行时环境 (`JavaRunTimeEnv`):**
|
||||||
|
|
||||||
|
| 方法 | 说明 |
|
||||||
|
|----------------------------------------|---------------|
|
||||||
|
| `JavaRunTimeEnv.JavaVersion()` | Java 版本号 |
|
||||||
|
| `JavaRunTimeEnv.JavaRuntimeEnvironmentVersion()` | 运行时环境版本号 |
|
||||||
|
| `JavaRunTimeEnv.JavaVMVersion()` | 虚拟机版本号 |
|
||||||
|
| `JavaRunTimeEnv.JavaBits()` | JDK 位数(如 64) |
|
||||||
|
| `JavaRunTimeEnv.JavaVMName()` | 虚拟机名称 |
|
||||||
|
|
||||||
|
```java
|
||||||
|
import top.gtb520.java.gnu.java.utils.library.utils.runtime.RunEnv;
|
||||||
|
import top.gtb520.java.gnu.java.utils.library.utils.runtime.JavaRunTimeEnv;
|
||||||
|
|
||||||
|
System.out.println("JAR路径: " + RunEnv.getJarPath());
|
||||||
|
System.out.println("临时目录: " + RunEnv.getTempDir());
|
||||||
|
System.out.println("新建临时目录: " + RunEnv.newTempDir());
|
||||||
|
System.out.println("JAR目录临时子目录: " + RunEnv.newJarThisTempDir());
|
||||||
|
System.out.println("Java版本: " + JavaRunTimeEnv.JavaVersion());
|
||||||
|
System.out.println("JVM位数: " + JavaRunTimeEnv.JavaBits());
|
||||||
|
```
|
||||||
|
|
||||||
|
也可以通过统一入口调用:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import top.gtb520.java.gnu.java.utils.library.main;
|
||||||
|
|
||||||
|
main.runtime.getJarPath(); // JAR 路径
|
||||||
|
main.runtime.getJavaVersion(); // Java 版本
|
||||||
|
main.runtime.getJavaRuntimeEnvironmentVersion(); // 运行时环境版本
|
||||||
|
main.runtime.getJavaVMVersion(); // 虚拟机版本
|
||||||
|
main.runtime.getJavaBits(); // JDK 位数
|
||||||
|
main.runtime.getJavaVMName(); // 虚拟机名称
|
||||||
|
|
||||||
|
// runtime.runenv 子接口
|
||||||
|
main.runtime.runenv.getTempDir(); // 系统临时目录
|
||||||
|
main.runtime.runenv.newTempDir(); // 新建临时目录
|
||||||
|
main.runtime.runenv.newJarThisTempDir(); // JAR 目录下新建临时目录
|
||||||
|
main.runtime.runenv.getJarThisTempDir(); // 同 newJarThisTempDir
|
||||||
```
|
```
|
||||||
|
|
||||||
## 构建
|
## 构建
|
||||||
@@ -167,35 +383,31 @@ public class Main {
|
|||||||
```bash
|
```bash
|
||||||
mvn compile
|
mvn compile
|
||||||
```
|
```
|
||||||
或
|
|
||||||
|
或打包为 JAR:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mvn package
|
mvn package
|
||||||
```
|
```
|
||||||
|
|
||||||
## 使用
|
## 使用
|
||||||
编译成功后,将 `target/gnu-java-utils-library-1.0.0.jar` 放入项目依赖中。</br>
|
|
||||||
或者在releases中下载最新版jar包,放入项目依赖中。
|
|
||||||
|
|
||||||
#### 使用示例:
|
编译成功后,将 `target/gnu-java-utils-library-1.4-SNAPSHOT.jar` 放入项目依赖中,或在 Releases 中下载最新版 JAR 包。
|
||||||
|
|
||||||
|
**pom.xml 引入本地依赖:**
|
||||||
|
|
||||||
**pom.xml**
|
|
||||||
```xml
|
```xml
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- 引入本地依赖,lib/gnu_java_utils_library-1.0-SNAPSHOT.jar -->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>top.gtb520.java.gnu</groupId>
|
<groupId>top.gtb520.java.gnu</groupId>
|
||||||
<artifactId>gnu_java_utils_library</artifactId>
|
<artifactId>gnu_java_utils_library</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.4-SNAPSHOT</version>
|
||||||
<scope>system</scope>
|
<scope>system</scope>
|
||||||
<!-- ${project.basedir} 为项目目录, /lib/ 为lib目录,xxxx.jar 依赖的Jar包-->
|
<systemPath>${project.basedir}/lib/gnu_java_utils_library-1.4-SNAPSHOT.jar</systemPath>
|
||||||
<systemPath>${project.basedir}/lib/gnu_java_utils_library-1.0-SNAPSHOT.jar</systemPath>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 异常处理
|
## 异常处理
|
||||||
|
|
||||||
压缩/解压方法在以下情况会抛出 `IllegalArgumentException`:
|
压缩/解压方法在以下情况会抛出 `IllegalArgumentException`:
|
||||||
@@ -203,6 +415,10 @@ mvn package
|
|||||||
- **压缩时**:源路径不存在
|
- **压缩时**:源路径不存在
|
||||||
- **解压时**:目标路径不是有效的目录
|
- **解压时**:目标路径不是有效的目录
|
||||||
|
|
||||||
|
随机数方法在以下情况会抛出 `IllegalArgumentException`:
|
||||||
|
|
||||||
|
- **起始值大于结束值时**
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
Apache License 2.0
|
Apache License 2.0
|
||||||
|
|||||||
8
pom.xml
8
pom.xml
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>top.gtb520.java.gnu</groupId>
|
<groupId>top.gtb520.java.gnu</groupId>
|
||||||
<artifactId>gnu_java_utils_library</artifactId>
|
<artifactId>gnu_java_utils_library</artifactId>
|
||||||
<version>1.2-SNAPSHOT</version>
|
<version>1.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>25</maven.compiler.source>
|
<maven.compiler.source>25</maven.compiler.source>
|
||||||
@@ -20,6 +20,12 @@
|
|||||||
<artifactId>commons-compress</artifactId>
|
<artifactId>commons-compress</artifactId>
|
||||||
<version>1.28.0</version>
|
<version>1.28.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>5.13.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
package top.gtb520.java.gnu.java.utils.library;
|
package top.gtb520.java.gnu.java.utils.library;
|
||||||
|
|
||||||
import top.gtb520.java.gnu.java.utils.library.utils.print.ColorNameEnum;
|
import top.gtb520.java.gnu.java.utils.library.utils.print.ColorNameEnum;
|
||||||
|
import top.gtb520.java.gnu.java.utils.library.utils.compress.CompressPNG.PNGThreshold;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static top.gtb520.java.gnu.java.utils.library.utils.file.CreateFile.BinFileType;
|
||||||
|
import static top.gtb520.java.gnu.java.utils.library.utils.file.EncryptionAndDecryption.EncryptionType;
|
||||||
|
|
||||||
|
|
||||||
public interface main {
|
public interface main {
|
||||||
// 压缩包操作相关接口与方法
|
// 压缩包操作相关接口与方法
|
||||||
@@ -24,6 +32,11 @@ public interface main {
|
|||||||
static void UnZip(String sourceFilePanth, String destFilePanth) throws Exception {
|
static void UnZip(String sourceFilePanth, String destFilePanth) throws Exception {
|
||||||
top.gtb520.java.gnu.java.utils.library.utils.compress.compress.UnZip(sourceFilePanth, destFilePanth);
|
top.gtb520.java.gnu.java.utils.library.utils.compress.compress.UnZip(sourceFilePanth, destFilePanth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 压缩png图片
|
||||||
|
static File CompressPNG(File pngFile, PNGThreshold threshold) {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.compress.CompressPNG.PNG(pngFile, threshold);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 终端打印接口与方法
|
// 终端打印接口与方法
|
||||||
@@ -63,4 +76,95 @@ public interface main {
|
|||||||
return top.gtb520.java.gnu.java.utils.library.utils.processing.hwid.getHWID.generateHWID();
|
return top.gtb520.java.gnu.java.utils.library.utils.processing.hwid.getHWID.generateHWID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 项目版本信息获取接口与方法
|
||||||
|
interface version {
|
||||||
|
static String getVersion() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.version.VERSION;
|
||||||
|
}
|
||||||
|
static String getJavaVersion() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.version.JAVA_VERSION;
|
||||||
|
}
|
||||||
|
static String getOpenJDKVersion() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.version.OpenJDK_VERSION;
|
||||||
|
}
|
||||||
|
static String getOpenJDKRuntimeEnvironment() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.version.OpenJDK_Runtime_Environment;
|
||||||
|
}
|
||||||
|
static String getOpenJDK64BitServerVM() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.version.OpenJDK_64Bit_Server_VM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 运行时信息获取接口与方法
|
||||||
|
interface runtime {
|
||||||
|
static String getJarPath() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.runtime.RunEnv.getJarPath();
|
||||||
|
}
|
||||||
|
static String getJavaVersion() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.runtime.JavaRunTimeEnv.JavaVersion();
|
||||||
|
}
|
||||||
|
static String getJavaRuntimeEnvironmentVersion() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.runtime.JavaRunTimeEnv.JavaRuntimeEnvironmentVersion();
|
||||||
|
}
|
||||||
|
static String getJavaVMVersion() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.runtime.JavaRunTimeEnv.JavaVMVersion();
|
||||||
|
}
|
||||||
|
static String getJavaBits() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.runtime.JavaRunTimeEnv.JavaBits();
|
||||||
|
}
|
||||||
|
static String getJavaVMName() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.runtime.JavaRunTimeEnv.JavaVMName();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 运行时等相关方法与接口
|
||||||
|
interface runenv {
|
||||||
|
// 获取运行时临时目录
|
||||||
|
static String newJarThisTempDir() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.runtime.RunEnv.newJarThisTempDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新建临时目录
|
||||||
|
static String newTempDir() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.runtime.RunEnv.newTempDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取临时目录
|
||||||
|
static String getTempDir() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.runtime.RunEnv.getTempDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前运行的JAR文件路径
|
||||||
|
static String getJarThisTempDir() {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.runtime.RunEnv.newJarThisTempDir();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件操作接口与方法
|
||||||
|
interface file {
|
||||||
|
interface CreateFile {
|
||||||
|
// 创建文件,创建空白文件,异步IO
|
||||||
|
static File NewFile(String FileName, boolean isBin, BinFileType binFileType) {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.file.CreateFile.NewFile(FileName, isBin, binFileType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以字节流方式保存文件,异步IO
|
||||||
|
static boolean StreamSaveFile(File file, Path TargePath, String FileName) {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.file.CreateFile.StreamSaveFile(file, TargePath, FileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件加解密操作
|
||||||
|
interface EncryptionAndDecryption {
|
||||||
|
// 文件加密
|
||||||
|
static File FileEncrypt(File file, EncryptionType type, Path keyPath) throws Exception {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.file.EncryptionAndDecryption.EncryptionFile(file, type, keyPath);
|
||||||
|
}
|
||||||
|
// 文件解密
|
||||||
|
static File DecryptionFile(File file, EncryptionType type, File keyfile) throws Exception {
|
||||||
|
return top.gtb520.java.gnu.java.utils.library.utils.file.EncryptionAndDecryption.DecryptionFile(file, type, keyfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package top.gtb520.java.gnu.java.utils.library.utils.compress;
|
||||||
|
|
||||||
|
import javax.imageio.IIOImage;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageWriteParam;
|
||||||
|
import javax.imageio.ImageWriter;
|
||||||
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
public class CompressPNG {
|
||||||
|
/***
|
||||||
|
* 压缩PNG图片
|
||||||
|
* 传入未压缩的PNG图片文件,和压缩阈值,返回压缩后的PNG图片文件
|
||||||
|
* 5个等级的压缩阈值,从低到高,THRESHOLD_1 最低,THRESHOLD_5 最高
|
||||||
|
*
|
||||||
|
* @param pngFile 未压缩的PNG图片文件
|
||||||
|
* @param threshold 压缩阈值
|
||||||
|
* @return 压缩后的PNG图片文件
|
||||||
|
*/
|
||||||
|
public static File PNG(File pngFile, PNGThreshold threshold) {
|
||||||
|
if (!pngFile.exists()) {
|
||||||
|
System.out.println("文件不存在");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (pngFile.getName().endsWith(".png") || pngFile.getName().endsWith(".PNG")) {
|
||||||
|
try {
|
||||||
|
BufferedImage image = ImageIO.read(pngFile);
|
||||||
|
if (image == null) {
|
||||||
|
System.out.println("无法读取PNG文件");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("png");
|
||||||
|
if (!writers.hasNext()) {
|
||||||
|
System.out.println("未找到PNG写入器");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ImageWriter writer = writers.next();
|
||||||
|
|
||||||
|
try (ImageOutputStream ios = ImageIO.createImageOutputStream(pngFile)) {
|
||||||
|
writer.setOutput(ios);
|
||||||
|
|
||||||
|
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||||
|
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||||
|
param.setCompressionQuality(getCompressionQuality(threshold));
|
||||||
|
|
||||||
|
writer.write(null, new IIOImage(image, null, null), param);
|
||||||
|
} finally {
|
||||||
|
writer.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pngFile;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println("文件不是PNG格式,无法压缩");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getCompressionQuality(PNGThreshold threshold) {
|
||||||
|
return switch (threshold) {
|
||||||
|
case THRESHOLD_1 -> 0.7f;
|
||||||
|
case THRESHOLD_2 -> 0.5f;
|
||||||
|
case THRESHOLD_3 -> 0.3f;
|
||||||
|
case THRESHOLD_4 -> 0.1f;
|
||||||
|
case THRESHOLD_5 -> 0.0f;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PNGThreshold {
|
||||||
|
THRESHOLD_1,
|
||||||
|
THRESHOLD_2,
|
||||||
|
THRESHOLD_3,
|
||||||
|
THRESHOLD_4,
|
||||||
|
THRESHOLD_5;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package top.gtb520.java.gnu.java.utils.library.utils.file;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
// 创建文件
|
||||||
|
public class CreateFile {
|
||||||
|
/***
|
||||||
|
* 创建文件,创建空白文件
|
||||||
|
* isBin为True时创建一个空白的二进制文件,否则创建一个空白的普通文件
|
||||||
|
* 异步IO操作
|
||||||
|
*
|
||||||
|
* @param FileName 文件名
|
||||||
|
* @param isBin 是否二进制文件
|
||||||
|
* @param binFileType 二进制文件类型,如果是普通文件则不需要设置次值,将其设置为null即可,
|
||||||
|
* 参考isBin参数,isBin参数为False则此参数设置为null
|
||||||
|
*
|
||||||
|
* @return 文件
|
||||||
|
*/
|
||||||
|
public static File NewFile(String FileName, boolean isBin, BinFileType binFileType) {
|
||||||
|
File TargetFile = null;
|
||||||
|
byte[] BufferByte = new byte[1024];
|
||||||
|
|
||||||
|
try {
|
||||||
|
TargetFile = new File(FileName);
|
||||||
|
if (TargetFile.createNewFile()) {
|
||||||
|
if (isBin && binFileType != null) {
|
||||||
|
// 根据二进制文件类型写入文件头标识
|
||||||
|
byte[] header = switch (binFileType) {
|
||||||
|
case ELF -> new byte[]{0x7F, 'E', 'L', 'F'};
|
||||||
|
case PE -> new byte[]{'M', 'Z'};
|
||||||
|
};
|
||||||
|
try (OutputStream os = Files.newOutputStream(TargetFile.toPath(),
|
||||||
|
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
|
||||||
|
os.write(header);
|
||||||
|
os.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
TargetFile = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TargetFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 以字节流方式保存文件
|
||||||
|
* 异步IO操作
|
||||||
|
*
|
||||||
|
* @param file 文件
|
||||||
|
* @param TargePath 目标路径
|
||||||
|
* @param FileName 文件名
|
||||||
|
*
|
||||||
|
* @return 是否保存成功,True为保存成功,False为保存失败
|
||||||
|
*/
|
||||||
|
public static boolean StreamSaveFile(File file, Path TargePath, String FileName) {
|
||||||
|
try {
|
||||||
|
Path targetFile = TargePath.resolve(FileName);
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
|
try (OutputStream os = Files.newOutputStream(targetFile,
|
||||||
|
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead;
|
||||||
|
try (var is = Files.newInputStream(file.toPath())) {
|
||||||
|
while ((bytesRead = is.read(buffer)) != -1) {
|
||||||
|
os.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.flush();
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}).get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BinFileType {
|
||||||
|
ELF, PE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
package top.gtb520.java.gnu.java.utils.library.utils.file;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
|
public class EncryptionAndDecryption {
|
||||||
|
/***
|
||||||
|
* 文件加密
|
||||||
|
* 异步执行加密操作
|
||||||
|
* 加密后覆盖原文件,不添加额外后缀
|
||||||
|
* 密钥保存到 keyPath 目录下,密钥名为 原文件名.key
|
||||||
|
*
|
||||||
|
* @param file 文件
|
||||||
|
* @param type 加密类型
|
||||||
|
* @param keyPath 密钥文件目录路径,密钥名为 文件名.key
|
||||||
|
* @return 加密后的文件(原文件),失败时返回 null
|
||||||
|
*/
|
||||||
|
public static File EncryptionFile(File file, EncryptionType type, Path keyPath) {
|
||||||
|
File TargetFile = null;
|
||||||
|
|
||||||
|
if (file == null || !file.exists() || !file.isFile()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (keyPath == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
TargetFile = CompletableFuture.supplyAsync(() -> {
|
||||||
|
try {
|
||||||
|
// 读取原文件内容
|
||||||
|
byte[] plainBytes = Files.readAllBytes(file.toPath());
|
||||||
|
|
||||||
|
// 根据加密类型生成对称密钥
|
||||||
|
SecretKey secretKey = generateSecretKey(type);
|
||||||
|
byte[] encryptedBytes = encryptBytes(plainBytes, secretKey, type);
|
||||||
|
|
||||||
|
// 将密钥写入 keyPath 目录,密钥名为 文件名.key
|
||||||
|
Files.createDirectories(keyPath);
|
||||||
|
String keyFileName = file.getName() + ".key";
|
||||||
|
Path keyFilePath = keyPath.resolve(keyFileName);
|
||||||
|
try (OutputStream kos = Files.newOutputStream(keyFilePath,
|
||||||
|
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
|
||||||
|
kos.write(secretKey.getEncoded());
|
||||||
|
kos.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密后覆盖写回原文件
|
||||||
|
try (OutputStream os = Files.newOutputStream(file.toPath(),
|
||||||
|
StandardOpenOption.TRUNCATE_EXISTING)) {
|
||||||
|
os.write(encryptedBytes);
|
||||||
|
os.flush();
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
TargetFile = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TargetFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 生成对称密钥
|
||||||
|
*
|
||||||
|
* @param type 加密类型
|
||||||
|
* @return 对称密钥
|
||||||
|
*/
|
||||||
|
private static SecretKey generateSecretKey(EncryptionType type) throws NoSuchAlgorithmException {
|
||||||
|
String algorithm = switch (type) {
|
||||||
|
case AES -> "AES";
|
||||||
|
case DES -> "DES";
|
||||||
|
case RSA -> "AES"; // RSA 为非对称加密,此处用 AES 做对称加密替代
|
||||||
|
};
|
||||||
|
KeyGenerator keyGen = KeyGenerator.getInstance(algorithm);
|
||||||
|
int keySize = switch (type) {
|
||||||
|
case AES -> 128;
|
||||||
|
case DES -> 56;
|
||||||
|
case RSA -> 128;
|
||||||
|
};
|
||||||
|
keyGen.init(keySize);
|
||||||
|
return keyGen.generateKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 对字节数据进行加密
|
||||||
|
*
|
||||||
|
* @param data 原始数据
|
||||||
|
* @param key 密钥
|
||||||
|
* @param type 加密类型
|
||||||
|
* @return 加密后的数据
|
||||||
|
*/
|
||||||
|
private static byte[] encryptBytes(byte[] data, SecretKey key, EncryptionType type)
|
||||||
|
throws NoSuchAlgorithmException, NoSuchPaddingException,
|
||||||
|
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
||||||
|
String algorithm = switch (type) {
|
||||||
|
case AES -> "AES/ECB/PKCS5Padding";
|
||||||
|
case DES -> "DES/ECB/PKCS5Padding";
|
||||||
|
case RSA -> "AES/ECB/PKCS5Padding";
|
||||||
|
};
|
||||||
|
Cipher cipher = Cipher.getInstance(algorithm);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||||
|
return cipher.doFinal(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 文件解密
|
||||||
|
* 异步执行解密操作
|
||||||
|
* 解密后覆盖写回原文件,不添加额外后缀
|
||||||
|
*
|
||||||
|
* @param file 加密文件
|
||||||
|
* @param type 加密类型,须与加密时一致
|
||||||
|
* @param keyfile 密钥文件,从中读取解密密钥
|
||||||
|
* @return 解密后的文件(原文件),失败时返回 null
|
||||||
|
*/
|
||||||
|
public static File DecryptionFile(File file, EncryptionType type, File keyfile) {
|
||||||
|
File TargetFile = null;
|
||||||
|
|
||||||
|
if (file == null || !file.exists() || !file.isFile()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (keyfile == null || !keyfile.exists() || !keyfile.isFile()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
TargetFile = CompletableFuture.supplyAsync(() -> {
|
||||||
|
try {
|
||||||
|
// 从密钥文件读取密钥
|
||||||
|
byte[] keyBytes = Files.readAllBytes(keyfile.toPath());
|
||||||
|
String algorithm = switch (type) {
|
||||||
|
case AES -> "AES";
|
||||||
|
case DES -> "DES";
|
||||||
|
case RSA -> "AES";
|
||||||
|
};
|
||||||
|
SecretKey secretKey = new javax.crypto.spec.SecretKeySpec(keyBytes, algorithm);
|
||||||
|
|
||||||
|
// 读取加密文件内容
|
||||||
|
byte[] encryptedBytes = Files.readAllBytes(file.toPath());
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
byte[] decryptedBytes = decryptBytes(encryptedBytes, secretKey, type);
|
||||||
|
|
||||||
|
// 解密后覆盖写回原文件
|
||||||
|
try (OutputStream os = Files.newOutputStream(file.toPath(),
|
||||||
|
StandardOpenOption.TRUNCATE_EXISTING)) {
|
||||||
|
os.write(decryptedBytes);
|
||||||
|
os.flush();
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
TargetFile = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TargetFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 对字节数据进行解密
|
||||||
|
*
|
||||||
|
* @param data 加密数据
|
||||||
|
* @param key 密钥
|
||||||
|
* @param type 加密类型
|
||||||
|
* @return 解密后的数据
|
||||||
|
*/
|
||||||
|
private static byte[] decryptBytes(byte[] data, SecretKey key, EncryptionType type)
|
||||||
|
throws NoSuchAlgorithmException, NoSuchPaddingException,
|
||||||
|
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
||||||
|
String algorithm = switch (type) {
|
||||||
|
case AES -> "AES/ECB/PKCS5Padding";
|
||||||
|
case DES -> "DES/ECB/PKCS5Padding";
|
||||||
|
case RSA -> "AES/ECB/PKCS5Padding";
|
||||||
|
};
|
||||||
|
Cipher cipher = Cipher.getInstance(algorithm);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, key);
|
||||||
|
return cipher.doFinal(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum EncryptionType {
|
||||||
|
AES, DES, RSA;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package top.gtb520.java.gnu.java.utils.library.utils.print;
|
package top.gtb520.java.gnu.java.utils.library.utils.print;
|
||||||
|
|
||||||
|
// 颜色名称枚举
|
||||||
public enum ColorNameEnum {
|
public enum ColorNameEnum {
|
||||||
RED, GREEN, YELLOW, BLUE, PURPLE, CYAN;
|
RED, GREEN, YELLOW, BLUE, PURPLE, CYAN;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package top.gtb520.java.gnu.java.utils.library.utils.runtime;
|
||||||
|
|
||||||
|
// 运行时Java环境信息
|
||||||
|
public class JavaRunTimeEnv {
|
||||||
|
|
||||||
|
// Java版本号
|
||||||
|
public static String JavaVersion() {
|
||||||
|
return System.getProperty("java.version");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Java运行时环境版本号
|
||||||
|
public static String JavaRuntimeEnvironmentVersion() {
|
||||||
|
return System.getProperty("java.runtime.version");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取虚拟机版本
|
||||||
|
public static String JavaVMVersion() {
|
||||||
|
return System.getProperty("java.vm.version");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 JDK 位数,64 位通常包含 amd64 或 64
|
||||||
|
public static String JavaBits() {
|
||||||
|
return System.getProperty("sun.arch.data.model");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取JavaVM名称
|
||||||
|
public static String JavaVMName() {
|
||||||
|
return System.getProperty("java.vm.name");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package top.gtb520.java.gnu.java.utils.library.utils.runtime;
|
||||||
|
|
||||||
|
import top.gtb520.java.gnu.java.utils.library.main;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
// 获取运行时的信息
|
||||||
|
public class RunEnv {
|
||||||
|
// 获取当前运行的JAR文件路径
|
||||||
|
public static String getJarPath() {
|
||||||
|
String path = main.class.getProtectionDomain().getCodeSource().getLocation().getPath();
|
||||||
|
if (System.getProperty("os.name").startsWith("Windows")) {
|
||||||
|
path = path.replace('/', '\\');
|
||||||
|
//去除路径最前面的 "\\"
|
||||||
|
path = path.substring(1);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取临时目录
|
||||||
|
public static String getTempDir() {
|
||||||
|
return System.getProperty("java.io.tmpdir");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新建临时目录,失败时返回 null
|
||||||
|
public static String newTempDir() {
|
||||||
|
return createTempDir(Path.of(getTempDir()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在 当前运行的JAR文件路径 下 新建临时目录,失败时返回 null
|
||||||
|
public static String newJarThisTempDir() {
|
||||||
|
Path parent = Path.of(getJarPath()).getParent();
|
||||||
|
if (parent == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return createTempDir(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在指定目录下创建以时间戳命名的临时目录,返回目录路径;目录已存在时返回 null
|
||||||
|
private static String createTempDir(Path parent) {
|
||||||
|
Path tempDir = parent.resolve(String.valueOf(System.currentTimeMillis()));
|
||||||
|
if (!tempDir.toFile().exists()) {
|
||||||
|
try {
|
||||||
|
tempDir.toFile().mkdirs();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return tempDir.toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package top.gtb520.java.gnu.java.utils.library;
|
||||||
|
|
||||||
|
public class version {
|
||||||
|
// 项目的版本信息,不代表实时运行环境
|
||||||
|
public static final String VERSION = "1.5";
|
||||||
|
public static final String JAVA_VERSION = "25";
|
||||||
|
public static final String OpenJDK_VERSION = "25.0.0.0.1";
|
||||||
|
public static final String OpenJDK_Runtime_Environment = "25.0.0.0.1+36-GA";
|
||||||
|
public static final String OpenJDK_64Bit_Server_VM = "25.0.0.0.1+36-GA";
|
||||||
|
|
||||||
|
public String GetVersion() {
|
||||||
|
return VERSION;
|
||||||
|
}
|
||||||
|
public String GetJavaVersion() {
|
||||||
|
return JAVA_VERSION;
|
||||||
|
}
|
||||||
|
public String GetOpenJDKVersion() {
|
||||||
|
return OpenJDK_VERSION;
|
||||||
|
}
|
||||||
|
public String GetOpenJDKRuntimeEnvironment() {
|
||||||
|
return OpenJDK_Runtime_Environment;
|
||||||
|
}
|
||||||
|
public String GetOpenJDK64BitServerVM() {
|
||||||
|
return OpenJDK_64Bit_Server_VM;
|
||||||
|
}
|
||||||
|
}
|
||||||
206
src/test/java/EncryptionAndDecryptionTest.java
Normal file
206
src/test/java/EncryptionAndDecryptionTest.java
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import top.gtb520.java.gnu.java.utils.library.utils.file.EncryptionAndDecryption;
|
||||||
|
import top.gtb520.java.gnu.java.utils.library.utils.file.EncryptionAndDecryption.EncryptionType;
|
||||||
|
|
||||||
|
public class EncryptionAndDecryptionTest {
|
||||||
|
|
||||||
|
static int passCount = 0;
|
||||||
|
static int failCount = 0;
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
System.out.println("========== EncryptionAndDecryption 测试开始 ==========");
|
||||||
|
|
||||||
|
// 正常场景:AES 加密/解密
|
||||||
|
testEncryptDecryptAES();
|
||||||
|
// 正常场景:DES 加密/解密
|
||||||
|
testEncryptDecryptDES();
|
||||||
|
// 正常场景:RSA 加密/解密
|
||||||
|
testEncryptDecryptRSA();
|
||||||
|
// 正常场景:验证密钥文件生成
|
||||||
|
testKeyFileGenerated();
|
||||||
|
// 正常场景:加密后文件内容发生变化
|
||||||
|
testFileContentChangedAfterEncrypt();
|
||||||
|
|
||||||
|
// 异常场景:file 为 null
|
||||||
|
testNullFile();
|
||||||
|
// 异常场景:keyPath 为 null
|
||||||
|
testNullKeyPath();
|
||||||
|
// 异常场景:keyfile 为 null(解密)
|
||||||
|
testNullKeyFile();
|
||||||
|
// 异常场景:文件不存在
|
||||||
|
testFileNotExist();
|
||||||
|
// 异常场景:密钥文件不存在(解密)
|
||||||
|
testKeyFileNotExist();
|
||||||
|
|
||||||
|
System.out.println("========== 测试结果 ==========");
|
||||||
|
System.out.println("通过: " + passCount);
|
||||||
|
System.out.println("失败: " + failCount);
|
||||||
|
System.out.println("总计: " + (passCount + failCount));
|
||||||
|
|
||||||
|
if (failCount > 0) {
|
||||||
|
System.out.println("存在失败的测试用例!");
|
||||||
|
System.exit(1);
|
||||||
|
} else {
|
||||||
|
System.out.println("全部测试通过!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建临时测试文件并写入测试内容
|
||||||
|
*/
|
||||||
|
static File createTempFile(String prefix) throws Exception {
|
||||||
|
Path tempDir = Files.createTempDirectory("enc_test_");
|
||||||
|
File tempFile = tempDir.resolve(prefix + ".txt").toFile();
|
||||||
|
Files.writeString(tempFile.toPath(), "Hello, EncryptionAndDecryption Test! 测试内容123");
|
||||||
|
return tempFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建临时密钥目录
|
||||||
|
*/
|
||||||
|
static Path createTempKeyPath() throws Exception {
|
||||||
|
return Files.createTempDirectory("enc_key_");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void assertEquals(Object expected, Object actual, String testName) {
|
||||||
|
if (expected == null && actual == null) {
|
||||||
|
passCount++;
|
||||||
|
System.out.println("[PASS] " + testName);
|
||||||
|
} else if (expected != null && expected.equals(actual)) {
|
||||||
|
passCount++;
|
||||||
|
System.out.println("[PASS] " + testName);
|
||||||
|
} else {
|
||||||
|
failCount++;
|
||||||
|
System.out.println("[FAIL] " + testName + " | 期望: " + expected + ", 实际: " + actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void assertTrue(boolean condition, String testName) {
|
||||||
|
if (condition) {
|
||||||
|
passCount++;
|
||||||
|
System.out.println("[PASS] " + testName);
|
||||||
|
} else {
|
||||||
|
failCount++;
|
||||||
|
System.out.println("[FAIL] " + testName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 正常场景 ====================
|
||||||
|
|
||||||
|
static void testEncryptDecryptAES() throws Exception {
|
||||||
|
String testName = "AES 加密后解密,内容还原";
|
||||||
|
File file = createTempFile("aes_test");
|
||||||
|
Path keyPath = createTempKeyPath();
|
||||||
|
String originalContent = Files.readString(file.toPath());
|
||||||
|
|
||||||
|
File encrypted = EncryptionAndDecryption.EncryptionFile(file, EncryptionType.AES, keyPath);
|
||||||
|
assertTrue(encrypted != null, testName + " - 加密返回非null");
|
||||||
|
|
||||||
|
File keyFile = keyPath.resolve(file.getName() + ".key").toFile();
|
||||||
|
File decrypted = EncryptionAndDecryption.DecryptionFile(file, EncryptionType.AES, keyFile);
|
||||||
|
assertTrue(decrypted != null, testName + " - 解密返回非null");
|
||||||
|
|
||||||
|
String decryptedContent = Files.readString(file.toPath());
|
||||||
|
assertEquals(originalContent, decryptedContent, testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testEncryptDecryptDES() throws Exception {
|
||||||
|
String testName = "DES 加密后解密,内容还原";
|
||||||
|
File file = createTempFile("des_test");
|
||||||
|
Path keyPath = createTempKeyPath();
|
||||||
|
String originalContent = Files.readString(file.toPath());
|
||||||
|
|
||||||
|
File encrypted = EncryptionAndDecryption.EncryptionFile(file, EncryptionType.DES, keyPath);
|
||||||
|
assertTrue(encrypted != null, testName + " - 加密返回非null");
|
||||||
|
|
||||||
|
File keyFile = keyPath.resolve(file.getName() + ".key").toFile();
|
||||||
|
File decrypted = EncryptionAndDecryption.DecryptionFile(file, EncryptionType.DES, keyFile);
|
||||||
|
assertTrue(decrypted != null, testName + " - 解密返回非null");
|
||||||
|
|
||||||
|
String decryptedContent = Files.readString(file.toPath());
|
||||||
|
assertEquals(originalContent, decryptedContent, testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testEncryptDecryptRSA() throws Exception {
|
||||||
|
String testName = "RSA 加密后解密,内容还原";
|
||||||
|
File file = createTempFile("rsa_test");
|
||||||
|
Path keyPath = createTempKeyPath();
|
||||||
|
String originalContent = Files.readString(file.toPath());
|
||||||
|
|
||||||
|
File encrypted = EncryptionAndDecryption.EncryptionFile(file, EncryptionType.RSA, keyPath);
|
||||||
|
assertTrue(encrypted != null, testName + " - 加密返回非null");
|
||||||
|
|
||||||
|
File keyFile = keyPath.resolve(file.getName() + ".key").toFile();
|
||||||
|
File decrypted = EncryptionAndDecryption.DecryptionFile(file, EncryptionType.RSA, keyFile);
|
||||||
|
assertTrue(decrypted != null, testName + " - 解密返回非null");
|
||||||
|
|
||||||
|
String decryptedContent = Files.readString(file.toPath());
|
||||||
|
assertEquals(originalContent, decryptedContent, testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testKeyFileGenerated() throws Exception {
|
||||||
|
String testName = "加密后密钥文件正确生成";
|
||||||
|
File file = createTempFile("key_test");
|
||||||
|
Path keyPath = createTempKeyPath();
|
||||||
|
|
||||||
|
EncryptionAndDecryption.EncryptionFile(file, EncryptionType.AES, keyPath);
|
||||||
|
|
||||||
|
File keyFile = keyPath.resolve(file.getName() + ".key").toFile();
|
||||||
|
assertTrue(keyFile.exists(), testName + " - 密钥文件存在");
|
||||||
|
assertTrue(keyFile.length() > 0, testName + " - 密钥文件非空");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testFileContentChangedAfterEncrypt() throws Exception {
|
||||||
|
String testName = "加密后文件内容与原文不同";
|
||||||
|
File file = createTempFile("content_test");
|
||||||
|
Path keyPath = createTempKeyPath();
|
||||||
|
byte[] originalBytes = Files.readAllBytes(file.toPath());
|
||||||
|
|
||||||
|
EncryptionAndDecryption.EncryptionFile(file, EncryptionType.AES, keyPath);
|
||||||
|
|
||||||
|
byte[] encryptedBytes = Files.readAllBytes(file.toPath());
|
||||||
|
assertTrue(!java.util.Arrays.equals(originalBytes, encryptedBytes), testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 异常场景 ====================
|
||||||
|
|
||||||
|
static void testNullFile() throws Exception {
|
||||||
|
String testName = "file 为 null 时加密返回 null";
|
||||||
|
Path keyPath = createTempKeyPath();
|
||||||
|
File result = EncryptionAndDecryption.EncryptionFile(null, EncryptionType.AES, keyPath);
|
||||||
|
assertEquals(null, result, testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testNullKeyPath() throws Exception {
|
||||||
|
String testName = "keyPath 为 null 时加密返回 null";
|
||||||
|
File file = createTempFile("null_keypath");
|
||||||
|
File result = EncryptionAndDecryption.EncryptionFile(file, EncryptionType.AES, null);
|
||||||
|
assertEquals(null, result, testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testNullKeyFile() throws Exception {
|
||||||
|
String testName = "keyfile 为 null 时解密返回 null";
|
||||||
|
File file = createTempFile("null_keyfile");
|
||||||
|
File result = EncryptionAndDecryption.DecryptionFile(file, EncryptionType.AES, null);
|
||||||
|
assertEquals(null, result, testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testFileNotExist() throws Exception {
|
||||||
|
String testName = "文件不存在时加密返回 null";
|
||||||
|
Path keyPath = createTempKeyPath();
|
||||||
|
File notExist = new File("non_existent_file_12345.txt");
|
||||||
|
File result = EncryptionAndDecryption.EncryptionFile(notExist, EncryptionType.AES, keyPath);
|
||||||
|
assertEquals(null, result, testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testKeyFileNotExist() throws Exception {
|
||||||
|
String testName = "密钥文件不存在时解密返回 null";
|
||||||
|
File file = createTempFile("no_keyfile");
|
||||||
|
File notExistKey = new File("non_existent_key_12345.key");
|
||||||
|
File result = EncryptionAndDecryption.DecryptionFile(file, EncryptionType.AES, notExistKey);
|
||||||
|
assertEquals(null, result, testName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
package top.gtb520.java.gnu.java.utils.library.utils.compress;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class CompressPNGTest {
|
||||||
|
|
||||||
|
static int passed = 0;
|
||||||
|
static int failed = 0;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("========== CompressPNG 测试开始 ==========");
|
||||||
|
|
||||||
|
testCompressWithEachThreshold();
|
||||||
|
testNonPNGFile();
|
||||||
|
testNonExistentFile();
|
||||||
|
testCompressedFileIsValidPNG();
|
||||||
|
testCompressedFileSmallerOrEqual();
|
||||||
|
|
||||||
|
System.out.println("========================================");
|
||||||
|
System.out.println("测试完成: 通过 " + passed + ", 失败 " + failed);
|
||||||
|
if (failed > 0) {
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试每个压缩阈值均能正常压缩并返回原文件
|
||||||
|
*/
|
||||||
|
static void testCompressWithEachThreshold() {
|
||||||
|
String testName = "testCompressWithEachThreshold";
|
||||||
|
for (CompressPNG.PNGThreshold threshold : CompressPNG.PNGThreshold.values()) {
|
||||||
|
File pngFile = null;
|
||||||
|
try {
|
||||||
|
pngFile = createTestPNG("test_threshold_" + threshold.name());
|
||||||
|
long originalSize = pngFile.length();
|
||||||
|
|
||||||
|
File result = CompressPNG.PNG(pngFile, threshold);
|
||||||
|
|
||||||
|
boolean returnedSameFile = result != null && result.equals(pngFile);
|
||||||
|
assertCondition(testName + " [" + threshold.name() + "] - 应返回原文件引用", returnedSameFile);
|
||||||
|
|
||||||
|
boolean fileExists = result != null && result.exists() && result.length() > 0;
|
||||||
|
assertCondition(testName + " [" + threshold.name() + "] - 压缩后文件应存在且非空", fileExists);
|
||||||
|
|
||||||
|
System.out.println(" [" + threshold.name() + "] 原始: " + originalSize + " bytes, 压缩后: " + (result != null ? result.length() : "N/A") + " bytes");
|
||||||
|
} catch (Exception e) {
|
||||||
|
assertCondition(testName + " [" + threshold.name() + "] - 不应抛出异常: " + e.getMessage(), false);
|
||||||
|
} finally {
|
||||||
|
deleteTestFile(pngFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试传入非PNG文件应返回null
|
||||||
|
*/
|
||||||
|
static void testNonPNGFile() {
|
||||||
|
String testName = "testNonPNGFile";
|
||||||
|
File txtFile = null;
|
||||||
|
try {
|
||||||
|
txtFile = File.createTempFile("test_not_png", ".txt");
|
||||||
|
java.nio.file.Files.writeString(txtFile.toPath(), "this is not a png");
|
||||||
|
|
||||||
|
File result = CompressPNG.PNG(txtFile, CompressPNG.PNGThreshold.THRESHOLD_3);
|
||||||
|
assertCondition(testName + " - 非PNG文件应返回null", result == null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
assertCondition(testName + " - 不应抛出异常: " + e.getMessage(), false);
|
||||||
|
} finally {
|
||||||
|
deleteTestFile(txtFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试传入不存在的文件应返回null
|
||||||
|
*/
|
||||||
|
static void testNonExistentFile() {
|
||||||
|
String testName = "testNonExistentFile";
|
||||||
|
try {
|
||||||
|
File nonExistent = new File(System.getProperty("java.io.tmpdir"), "non_existent_file.png");
|
||||||
|
File result = CompressPNG.PNG(nonExistent, CompressPNG.PNGThreshold.THRESHOLD_3);
|
||||||
|
assertCondition(testName + " - 不存在的文件应返回null", result == null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
assertCondition(testName + " - 不应抛出异常: " + e.getMessage(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试压缩后的文件仍然是有效的PNG图片
|
||||||
|
*/
|
||||||
|
static void testCompressedFileIsValidPNG() {
|
||||||
|
String testName = "testCompressedFileIsValidPNG";
|
||||||
|
File pngFile = null;
|
||||||
|
try {
|
||||||
|
pngFile = createTestPNG("test_valid_png");
|
||||||
|
CompressPNG.PNG(pngFile, CompressPNG.PNGThreshold.THRESHOLD_3);
|
||||||
|
|
||||||
|
BufferedImage compressed = ImageIO.read(pngFile);
|
||||||
|
boolean isValidPNG = compressed != null;
|
||||||
|
assertCondition(testName + " - 压缩后的文件应是有效的PNG图片", isValidPNG);
|
||||||
|
|
||||||
|
if (isValidPNG) {
|
||||||
|
assertCondition(testName + " - 压缩后图片宽度应一致", compressed.getWidth() == 100);
|
||||||
|
assertCondition(testName + " - 压缩后图片高度应一致", compressed.getHeight() == 100);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
assertCondition(testName + " - 不应抛出异常: " + e.getMessage(), false);
|
||||||
|
} finally {
|
||||||
|
deleteTestFile(pngFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试压缩后文件大小不超过原始文件
|
||||||
|
*/
|
||||||
|
static void testCompressedFileSmallerOrEqual() {
|
||||||
|
String testName = "testCompressedFileSmallerOrEqual";
|
||||||
|
File pngFile = null;
|
||||||
|
try {
|
||||||
|
// 创建一个较大的、有压缩空间的测试图片(纯色+渐变)
|
||||||
|
pngFile = createLargeTestPNG("test_compression_ratio", 500, 500);
|
||||||
|
long originalSize = pngFile.length();
|
||||||
|
|
||||||
|
File result = CompressPNG.PNG(pngFile, CompressPNG.PNGThreshold.THRESHOLD_5);
|
||||||
|
|
||||||
|
if (result != null) {
|
||||||
|
long compressedSize = result.length();
|
||||||
|
assertCondition(testName + " - 压缩后文件大小(" + compressedSize + ")应不超过原始大小(" + originalSize + ")",
|
||||||
|
compressedSize <= originalSize);
|
||||||
|
System.out.println(" 原始: " + originalSize + " bytes, THRESHOLD_5压缩后: " + compressedSize + " bytes");
|
||||||
|
} else {
|
||||||
|
assertCondition(testName + " - 压缩不应返回null", false);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
assertCondition(testName + " - 不应抛出异常: " + e.getMessage(), false);
|
||||||
|
} finally {
|
||||||
|
deleteTestFile(pngFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 辅助方法 ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个100x100的测试PNG图片
|
||||||
|
*/
|
||||||
|
static File createTestPNG(String name) throws IOException {
|
||||||
|
File file = File.createTempFile(name, ".png");
|
||||||
|
BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
|
||||||
|
Graphics2D g2d = image.createGraphics();
|
||||||
|
// 绘制渐变图案,使图片有一定的压缩空间
|
||||||
|
for (int y = 0; y < 100; y++) {
|
||||||
|
for (int x = 0; x < 100; x++) {
|
||||||
|
image.setRGB(x, y, new Color(x * 2, y * 2, (x + y)).getRGB());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g2d.dispose();
|
||||||
|
ImageIO.write(image, "png", file);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个指定尺寸的测试PNG图片(包含更多颜色变化以增加压缩效果差异)
|
||||||
|
*/
|
||||||
|
static File createLargeTestPNG(String name, int width, int height) throws IOException {
|
||||||
|
File file = File.createTempFile(name, ".png");
|
||||||
|
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||||
|
Graphics2D g2d = image.createGraphics();
|
||||||
|
// 绘制大面积纯色区域(压缩率高)
|
||||||
|
g2d.setColor(Color.WHITE);
|
||||||
|
g2d.fillRect(0, 0, width, height);
|
||||||
|
g2d.setColor(Color.RED);
|
||||||
|
g2d.fillRect(0, 0, width / 2, height / 2);
|
||||||
|
g2d.setColor(Color.BLUE);
|
||||||
|
g2d.fillRect(width / 2, 0, width / 2, height / 2);
|
||||||
|
g2d.setColor(Color.GREEN);
|
||||||
|
g2d.fillRect(0, height / 2, width / 2, height / 2);
|
||||||
|
g2d.setColor(Color.YELLOW);
|
||||||
|
g2d.fillRect(width / 2, height / 2, width / 2, height / 2);
|
||||||
|
g2d.dispose();
|
||||||
|
ImageIO.write(image, "png", file);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全删除测试文件
|
||||||
|
*/
|
||||||
|
static void deleteTestFile(File file) {
|
||||||
|
if (file != null && file.exists()) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void assertCondition(String message, boolean condition) {
|
||||||
|
if (condition) {
|
||||||
|
System.out.println("[PASS] " + message);
|
||||||
|
passed++;
|
||||||
|
} else {
|
||||||
|
System.out.println("[FAIL] " + message);
|
||||||
|
failed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package top.gtb520.java.gnu.java.utils.library.utils.runtime;
|
||||||
|
|
||||||
|
import static top.gtb520.java.gnu.java.utils.library.utils.runtime.RunEnv.*;
|
||||||
|
|
||||||
|
class RunEnvTest {
|
||||||
|
public static void main() {
|
||||||
|
System.out.println(getJarPath());
|
||||||
|
System.out.printf(newJarThisTempDir() + "\n");
|
||||||
|
System.out.println(newTempDir());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user