minify-pic-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -0
- package/index.js +162 -0
- package/package.json +26 -0
package/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# TinyPic CLI - 图片批量压缩工具
|
|
2
|
+
|
|
3
|
+
一个简单易用的图片批量压缩命令行工具,支持 PNG、JPEG、GIF 格式,适合前端和设计师快速优化图片体积。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
- 🖼️ 支持多种图片格式:PNG、JPEG、GIF
|
|
8
|
+
- 📁 批量处理目录及子目录中的所有图片
|
|
9
|
+
- ⚙️ 可自定义压缩质量和参数
|
|
10
|
+
- 🚫 支持排除指定目录
|
|
11
|
+
- 📊 显示压缩前后文件大小对比
|
|
12
|
+
- 🎯 保持原有目录结构
|
|
13
|
+
|
|
14
|
+
## 安装
|
|
15
|
+
|
|
16
|
+
### 全局安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# 使用 pnpm 安装
|
|
20
|
+
pnpm install -g tinypic-cli
|
|
21
|
+
|
|
22
|
+
# 或使用 npm
|
|
23
|
+
npm install -g tinypic-cli
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 使用方法
|
|
27
|
+
|
|
28
|
+
### 基本用法
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# 压缩当前目录的所有图片
|
|
32
|
+
mpic
|
|
33
|
+
|
|
34
|
+
# 或直接运行
|
|
35
|
+
node index.js
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 命令行选项
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
mpic [选项]
|
|
42
|
+
|
|
43
|
+
选项:
|
|
44
|
+
-d, --dir <dir> 需要压缩的目录 (默认: 当前目录)
|
|
45
|
+
-o, --output <output> 输出目录 (默认: ./output)
|
|
46
|
+
-q, --quality <quality> 压缩质量 0-100 (默认: 80)
|
|
47
|
+
-g, --gif-colours <colours> GIF调色板最大数量 2-256 (默认: 128)
|
|
48
|
+
-b, --black-dirs <dirs> 排除的子文件夹名称,逗号分隔 (默认: "no")
|
|
49
|
+
-v, --version 显示版本号
|
|
50
|
+
-h, --help 显示帮助信息
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 使用示例
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# 压缩指定目录的图片,输出到 compressed 文件夹
|
|
57
|
+
mpic -d ./images -o ./compressed
|
|
58
|
+
|
|
59
|
+
# 设置压缩质量为 90
|
|
60
|
+
mpic -q 90
|
|
61
|
+
|
|
62
|
+
# 排除 node_modules 和 .git 目录
|
|
63
|
+
mpic -b "node_modules,.git"
|
|
64
|
+
|
|
65
|
+
# 设置 GIF 调色板为 64 色
|
|
66
|
+
mpic -g 64
|
|
67
|
+
|
|
68
|
+
# 组合使用多个选项
|
|
69
|
+
mpic -d ./src/assets -o ./dist/assets -q 85 -b "node_modules,.git"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 支持的图片格式
|
|
73
|
+
|
|
74
|
+
- **PNG**: 支持透明度,适合图标和简单图形
|
|
75
|
+
- **JPEG**: 适合照片和复杂图像
|
|
76
|
+
- **GIF**: 支持动画,可调整调色板颜色数量
|
|
77
|
+
|
|
78
|
+
## 配置说明
|
|
79
|
+
|
|
80
|
+
### 压缩质量 (quality)
|
|
81
|
+
- 范围:0-100
|
|
82
|
+
- 默认:80
|
|
83
|
+
- 数值越高,质量越好,文件越大
|
|
84
|
+
- 建议:80-90 为最佳平衡点
|
|
85
|
+
|
|
86
|
+
### GIF 调色板颜色数 (gif-colours)
|
|
87
|
+
- 范围:2-256
|
|
88
|
+
- 默认:128
|
|
89
|
+
- 颜色数越少,文件越小,但可能影响视觉效果
|
|
90
|
+
- 建议:64-128 为常用范围
|
|
91
|
+
|
|
92
|
+
### 排除目录 (black-dirs)
|
|
93
|
+
- 支持多个目录,用逗号分隔
|
|
94
|
+
- 默认排除 "no" 目录
|
|
95
|
+
- 常用排除目录:`node_modules,.git,dist,build`
|
|
96
|
+
|
|
97
|
+
## 输出说明
|
|
98
|
+
|
|
99
|
+
工具会在控制台显示:
|
|
100
|
+
- 当前工作目录
|
|
101
|
+
- 压缩进度和结果
|
|
102
|
+
- 文件大小变化对比
|
|
103
|
+
- 最终输出目录
|
|
104
|
+
|
|
105
|
+
示例输出:
|
|
106
|
+
```
|
|
107
|
+
当前目录路径为: /path/to/your/project
|
|
108
|
+
是否需要压缩当前目录的所有图片?Y/N:y
|
|
109
|
+
压缩完成 [大小变化: 2.5MB ---->>>> 1.2MB] /path/to/output/image1.jpg
|
|
110
|
+
压缩完成 [大小变化: 800KB ---->>>> 400KB] /path/to/output/image2.png
|
|
111
|
+
压缩任务全部完成,已输出至 /path/to/output
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## 注意事项
|
|
115
|
+
|
|
116
|
+
1. **备份原文件**:工具会创建新的压缩文件,不会覆盖原文件
|
|
117
|
+
2. **输出目录**:默认输出到 `./output` 目录,会自动创建
|
|
118
|
+
3. **目录结构**:压缩后会保持原有的目录结构
|
|
119
|
+
4. **大文件处理**:对于大文件,压缩可能需要一些时间
|
|
120
|
+
5. **权限问题**:确保对目标目录有写入权限
|
|
121
|
+
|
|
122
|
+
## 技术栈
|
|
123
|
+
|
|
124
|
+
- **Node.js**: 运行环境
|
|
125
|
+
- **Sharp**: 高性能图片处理库
|
|
126
|
+
- **Commander.js**: 命令行参数解析
|
|
127
|
+
- **Readline**: 用户交互
|
|
128
|
+
|
|
129
|
+
## 开发
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# 安装依赖
|
|
133
|
+
pnpm install
|
|
134
|
+
|
|
135
|
+
# 运行开发版本
|
|
136
|
+
node index.js
|
|
137
|
+
|
|
138
|
+
# 构建
|
|
139
|
+
pnpm run build
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## 许可证
|
|
143
|
+
|
|
144
|
+
ISC
|
|
145
|
+
|
|
146
|
+
## 贡献
|
|
147
|
+
|
|
148
|
+
欢迎提交 Issue 和 Pull Request!
|
|
149
|
+
|
|
150
|
+
## 更新日志
|
|
151
|
+
|
|
152
|
+
### v1.0.0
|
|
153
|
+
- 初始版本发布
|
|
154
|
+
- 支持 PNG、JPEG、GIF 格式压缩
|
|
155
|
+
- 命令行参数支持
|
|
156
|
+
- 批量处理功能
|
package/index.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const sharp = require("sharp");
|
|
5
|
+
const readline = require("readline");
|
|
6
|
+
const { Command } = require("commander");
|
|
7
|
+
|
|
8
|
+
// 默认配置参数
|
|
9
|
+
const DEFAULT_CONFIG = {
|
|
10
|
+
targetDir: process.cwd(), // 需要压缩的目录(可包含子目录)
|
|
11
|
+
outputDir: path.join(process.cwd(), "output"), // 输出目录
|
|
12
|
+
quality: 80, // 压缩质量 0 - 100
|
|
13
|
+
gifColours: 128, // GIF调色板最大数量
|
|
14
|
+
blackDirs: ["no"], // 排除的子文件夹名称
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// 询问用户是否继续
|
|
18
|
+
function askUserToContinue() {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
const rl = readline.createInterface({
|
|
21
|
+
input: process.stdin,
|
|
22
|
+
output: process.stdout,
|
|
23
|
+
});
|
|
24
|
+
console.log(`当前目录路径为: ${process.cwd()}`);
|
|
25
|
+
rl.question("是否需要压缩当前目录的所有图片?Y/N:", (answer) => {
|
|
26
|
+
rl.close();
|
|
27
|
+
resolve(answer.trim().toLowerCase() === "y");
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 获取文件大小(带单位,自动转换MB/KB)
|
|
33
|
+
function getFileSizeWithUnit(filePath) {
|
|
34
|
+
const stats = fs.statSync(filePath);
|
|
35
|
+
const sizeInBytes = stats.size;
|
|
36
|
+
const sizeInMB = sizeInBytes / 1024 / 1024;
|
|
37
|
+
if (sizeInMB >= 1) {
|
|
38
|
+
return sizeInMB.toFixed(2) + "MB";
|
|
39
|
+
} else {
|
|
40
|
+
const sizeInKB = sizeInBytes / 1024;
|
|
41
|
+
if (sizeInKB < 0.01 && sizeInBytes > 0) {
|
|
42
|
+
return "<0.01KB";
|
|
43
|
+
}
|
|
44
|
+
return sizeInKB.toFixed(2) + "KB";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 压缩单张图片
|
|
49
|
+
async function compressImage(filePath, config, baseDir = config.targetDir) {
|
|
50
|
+
const beforeSize = getFileSizeWithUnit(filePath);
|
|
51
|
+
sharp.cache(false);
|
|
52
|
+
|
|
53
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
54
|
+
let sharpInstance;
|
|
55
|
+
|
|
56
|
+
switch (ext) {
|
|
57
|
+
case ".png":
|
|
58
|
+
sharpInstance = sharp(filePath).png({ quality: config.quality });
|
|
59
|
+
break;
|
|
60
|
+
case ".jpg":
|
|
61
|
+
case ".jpeg":
|
|
62
|
+
sharpInstance = sharp(filePath).jpeg({ quality: config.quality });
|
|
63
|
+
break;
|
|
64
|
+
case ".gif":
|
|
65
|
+
sharpInstance = sharp(filePath, {
|
|
66
|
+
animated: true,
|
|
67
|
+
limitInputPixels: false,
|
|
68
|
+
}).gif({ colours: config.gifColours });
|
|
69
|
+
break;
|
|
70
|
+
default:
|
|
71
|
+
throw new Error(`不支持的文件类型: ${ext}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 保持目录结构
|
|
75
|
+
const relativePath = path.relative(baseDir, filePath);
|
|
76
|
+
const outputFilePath = path.join(config.outputDir, relativePath);
|
|
77
|
+
const outputFileDir = path.dirname(outputFilePath);
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
if (!fs.existsSync(outputFileDir)) {
|
|
81
|
+
fs.mkdirSync(outputFileDir, { recursive: true });
|
|
82
|
+
}
|
|
83
|
+
const buffer = await sharpInstance.toBuffer();
|
|
84
|
+
fs.writeFileSync(outputFilePath, buffer);
|
|
85
|
+
fs.chmodSync(outputFilePath, 0o646);
|
|
86
|
+
const afterSize = getFileSizeWithUnit(outputFilePath);
|
|
87
|
+
console.log(
|
|
88
|
+
`压缩完成 [大小变化: ${beforeSize} ---->>>> ${afterSize}] ${outputFilePath}`
|
|
89
|
+
);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error(`压缩出错: ${outputFilePath}`, error);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 递归压缩目录下所有图片
|
|
96
|
+
async function compressFiles(dir, config, baseDir = config.targetDir) {
|
|
97
|
+
const files = fs.readdirSync(dir);
|
|
98
|
+
|
|
99
|
+
for (const file of files) {
|
|
100
|
+
const filePath = path.join(dir, file);
|
|
101
|
+
const stat = fs.statSync(filePath);
|
|
102
|
+
|
|
103
|
+
// 跳过 output 目录
|
|
104
|
+
if (filePath === config.outputDir) {
|
|
105
|
+
console.log(`跳过的目录: ${filePath}(为输出目录)`);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (stat.isDirectory()) {
|
|
110
|
+
if (!config.blackDirs.includes(file)) {
|
|
111
|
+
await compressFiles(filePath, config, baseDir);
|
|
112
|
+
} else {
|
|
113
|
+
console.log(`跳过的目录: ${filePath}`);
|
|
114
|
+
}
|
|
115
|
+
} else if (/\.(png|jpe?g|gif)$/i.test(filePath)) {
|
|
116
|
+
await compressImage(filePath, config, baseDir);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// commander命令行包装
|
|
122
|
+
const program = new Command();
|
|
123
|
+
|
|
124
|
+
program
|
|
125
|
+
.name("mpic")
|
|
126
|
+
.description("图片批量压缩工具")
|
|
127
|
+
.option("-d, --dir <dir>", "需要压缩的目录", DEFAULT_CONFIG.targetDir)
|
|
128
|
+
.option("-o, --output <output>", "输出目录", DEFAULT_CONFIG.outputDir)
|
|
129
|
+
.option("-q, --quality <quality>", "压缩质量(0-100)", String(DEFAULT_CONFIG.quality))
|
|
130
|
+
.option("-g, --gif-colours <colours>", "GIF调色板最大数量(2-256)", String(DEFAULT_CONFIG.gifColours))
|
|
131
|
+
.option("-b, --black-dirs <dirs>", "排除的子文件夹名称(逗号分隔)", val => val.split(","), DEFAULT_CONFIG.blackDirs)
|
|
132
|
+
.version(require('./package.json').version, '-v, --version', '显示版本号')
|
|
133
|
+
.action(async (options) => {
|
|
134
|
+
// 合并配置
|
|
135
|
+
const config = {
|
|
136
|
+
targetDir: path.resolve(process.cwd(), options.dir),
|
|
137
|
+
outputDir: path.resolve(process.cwd(), options.output),
|
|
138
|
+
quality: parseInt(options.quality, 10),
|
|
139
|
+
gifColours: parseInt(options.gifColours, 10),
|
|
140
|
+
blackDirs: Array.isArray(options.blackDirs) ? options.blackDirs : [options.blackDirs],
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
let shouldContinue = options.yes;
|
|
144
|
+
if (!shouldContinue) {
|
|
145
|
+
shouldContinue = await askUserToContinue();
|
|
146
|
+
}
|
|
147
|
+
if (!shouldContinue) {
|
|
148
|
+
console.log("已取消操作。");
|
|
149
|
+
process.exit(0);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const inputDir = config.targetDir;
|
|
153
|
+
compressFiles(inputDir, config, inputDir)
|
|
154
|
+
.then(() => {
|
|
155
|
+
console.log("压缩任务全部完成,已输出至", config.outputDir);
|
|
156
|
+
})
|
|
157
|
+
.catch((err) => {
|
|
158
|
+
console.error("压缩文件时出错:", err);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
program.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "minify-pic-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "一个简单易用的图片批量压缩命令行工具,支持 PNG、JPEG、GIF 格式,适合前端和设计师快速优化图片体积。",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mpic": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "node ./index.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"图片压缩",
|
|
14
|
+
"命令行工具",
|
|
15
|
+
"image",
|
|
16
|
+
"compress",
|
|
17
|
+
"cli",
|
|
18
|
+
"sharp"
|
|
19
|
+
],
|
|
20
|
+
"author": "kevin031",
|
|
21
|
+
"license": "ISC",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"commander": "^14.0.0",
|
|
24
|
+
"sharp": "^0.33.5"
|
|
25
|
+
}
|
|
26
|
+
}
|