Go语言实战案例 — 工具开发篇:实现一个图片批量压缩工具

发布于:2025-09-12 ⋅ 阅读:(17) ⋅ 点赞:(0)

在日常开发和办公中,图片体积过大会带来不少困扰:网页加载慢、存储占用大、邮件难以发送。很多时候,我们并不需要极致的画质,而是需要在清晰度和体积之间找到平衡。今天我们就用 Go 写一个图片批量压缩工具,从设计到实现,完整走一遍。


功能目标

  • • 批量处理:支持整个目录下的所有图片文件(支持递归)。
  • • 格式支持:JPEG、PNG、WebP。
  • • 自定义压缩质量:用户可以通过参数指定压缩比。
  • • 输出目录可选:压缩后的图片保存到指定文件夹,默认生成 output
  • • 保留文件结构:输出文件夹下的路径结构与原始一致。
  • • 简单易用:命令行参数运行,不依赖复杂环境。

技术选型

  • • 标准库:imageimage/jpegimage/png
  • • 第三方库:
    • • github.com/chai2010/webp —— WebP 编解码
  • • Go 并发:使用 goroutine + worker pool 来提升批量处理效率。

项目结构(示意)

img-compressor/
├── main.go
├── go.mod

完整代码(main.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
}

使用方法

  1. 1. 初始化项目并安装依赖:
go mod init img-compressor
go get github.com/chai2010/webp
  1. 2. 放置图片到 ./images 目录。
  2. 3. 运行程序:
go run main.go
  1. 4. 查看压缩结果:
    所有压缩后的图片会保存在 ./output 目录下,目录结构保持不变。

实践要点与注意事项

  • • 质量参数:JPEG 和 WebP 可以用 0-100 来控制压缩比;PNG 主要通过压缩等级控制体积,但效果有限。
  • • 批量优化:若图片数量很多,可以用 goroutine worker pool 提升效率,避免一次性开太多协程。
  • • 格式兼容:WebP 在一些老旧系统和浏览器上兼容性不足,若是做网页资源,需要配合 fallback。
  • • 压缩策略:有时候先统一缩放图片(比如长边 1080px),再压缩比单纯调整质量更省空间。

进一步扩展

  • • 添加 命令行参数(例如用 flag 或 cobra),让用户可以指定输入目录、输出目录、质量、递归开关等。
  • • 支持 缩放功能(如最大宽高)。
  • • 输出 统计信息(压缩前后大小对比,总共节省空间)。
  • • 打包为跨平台可执行文件,方便非技术人员直接使用。


网站公告

今日签到

点亮在社区的每一天
去签到