在日常开发和办公中,图片体积过大会带来不少困扰:网页加载慢、存储占用大、邮件难以发送。很多时候,我们并不需要极致的画质,而是需要在清晰度和体积之间找到平衡 。今天我们就用 Go 写一个图片批量压缩工具,从设计到实现,完整走一遍。
功能目标
- 批量处理:支持整个目录下的所有图片文件(支持递归)。
- 格式支持:JPEG、PNG、WebP。
- 自定义压缩质量:用户可以通过参数指定压缩比。
- 输出目录可选 :压缩后的图片保存到指定文件夹,默认生成
output
。 - 保留文件结构:输出文件夹下的路径结构与原始一致。
- 简单易用:命令行参数运行,不依赖复杂环境。
技术选型
-
标准库:
image
、image/jpeg
、image/png
-
第三方库:
- github.com/chai2010/we... ------ WebP 编解码
-
Go 并发:使用 goroutine + worker pool 来提升批量处理效率。
项目结构(示意)
css
img-compressor/
├── main.go
├── go.mod
完整代码(main.go)
go
package main
import (
"fmt"
"image"
"image/jpeg"
"image/png"
"os"
"path/filepath"
"strings"
"sync"
"github.com/chai2010/webp"
)
type Options struct {
InputDir string
OutputDir string
Quality int
Workers int
Recursive bool
}
func main() {
opts := Options{
InputDir: "./images", // 输入目录
OutputDir: "./output", // 输出目录
Quality: 75, // 压缩质量(0-100)
Workers: 4, // 并发 worker 数
Recursive: true, // 是否递归
}
if err := run(opts); err != nil {
fmt.Println("❌ 发生错误:", err)
os.Exit(1)
}
fmt.Println("✅ 批量压缩完成!")
}
func run(opts Options) error {
// 收集所有待处理文件
var files []string
err := filepath.Walk(opts.InputDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
if path != opts.InputDir && !opts.Recursive {
return filepath.SkipDir
}
return nil
}
ext := strings.ToLower(filepath.Ext(info.Name()))
if ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".webp" {
files = append(files, path)
}
return nil
})
if err != nil {
return err
}
if len(files) == 0 {
return fmt.Errorf("没有找到任何图片文件")
}
// worker pool
fileCh := make(chan string)
var wg sync.WaitGroup
for i := 0; i < opts.Workers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for f := range fileCh {
if err := compressImage(f, opts); err != nil {
fmt.Printf("⚠️ 压缩失败 %s: %v\n", f, err)
} else {
fmt.Printf("✨ 压缩完成: %s\n", f)
}
}
}()
}
for _, f := range files {
fileCh <- f
}
close(fileCh)
wg.Wait()
return nil
}
// 压缩单张图片
func compressImage(path string, opts Options) error {
inFile, err := os.Open(path)
if err != nil {
return err
}
defer inFile.Close()
img, format, err := image.Decode(inFile)
if err != nil {
return fmt.Errorf("解码失败: %w", err)
}
// 输出路径
rel, _ := filepath.Rel(opts.InputDir, path)
outPath := filepath.Join(opts.OutputDir, rel)
if err := os.MkdirAll(filepath.Dir(outPath), 0755); err != nil {
return err
}
outFile, err := os.Create(outPath)
if err != nil {
return err
}
defer outFile.Close()
switch format {
case "jpeg":
err = jpeg.Encode(outFile, img, &jpeg.Options{Quality: opts.Quality})
case "png":
encoder := png.Encoder{CompressionLevel: png.BestCompression}
err = encoder.Encode(outFile, img)
case "webp":
err = webp.Encode(outFile, img, &webp.Options{Quality: float32(opts.Quality)})
default:
return fmt.Errorf("不支持的格式: %s", format)
}
return err
}
使用方法
- 初始化项目并安装依赖:
bash
go mod init img-compressor
go get github.com/chai2010/webp
-
放置图片到
./images
目录。 -
运行程序:
bash
go run main.go
- 查看压缩结果: 所有压缩后的图片会保存在
./output
目录下,目录结构保持不变。
实践要点与注意事项
- 质量参数 :JPEG 和 WebP 可以用
0-100
来控制压缩比;PNG 主要通过压缩等级控制体积,但效果有限。 - 批量优化:若图片数量很多,可以用 goroutine worker pool 提升效率,避免一次性开太多协程。
- 格式兼容:WebP 在一些老旧系统和浏览器上兼容性不足,若是做网页资源,需要配合 fallback。
- 压缩策略:有时候先统一缩放图片(比如长边 1080px),再压缩比单纯调整质量更省空间。
进一步扩展
- 添加 命令行参数 (例如用
flag
或cobra
),让用户可以指定输入目录、输出目录、质量、递归开关等。 - 支持 缩放功能(如最大宽高)。
- 输出 统计信息(压缩前后大小对比,总共节省空间)。
- 打包为跨平台可执行文件,方便非技术人员直接使用。