Go語言開發(fā)實(shí)現(xiàn)一個(gè)圖片批量壓縮工具
在日常開發(fā)和辦公中,圖片體積過大會(huì)帶來不少困擾:網(wǎng)頁加載慢、存儲(chǔ)占用大、郵件難以發(fā)送。很多時(shí)候,我們并不需要極致的畫質(zhì),而是需要在清晰度和體積之間找到平衡。今天我們就用 Go 寫一個(gè)圖片批量壓縮工具,從設(shè)計(jì)到實(shí)現(xiàn),完整走一遍。
功能目標(biāo)
- 批量處理:支持整個(gè)目錄下的所有圖片文件(支持遞歸)。
- 格式支持:JPEG、PNG、WebP。
- 自定義壓縮質(zhì)量:用戶可以通過參數(shù)指定壓縮比。
- 輸出目錄可選:壓縮后的圖片保存到指定文件夾,默認(rèn)生成
output。 - 保留文件結(jié)構(gòu):輸出文件夾下的路徑結(jié)構(gòu)與原始一致。
- 簡單易用:命令行參數(shù)運(yùn)行,不依賴復(fù)雜環(huán)境。
技術(shù)選型
標(biāo)準(zhǔn)庫:image、image/jpeg、image/png
第三方庫:
github.com/chai2010/webp —— WebP 編解碼
Go 并發(fā):使用 goroutine + worker pool 來提升批量處理效率。
項(xiàng)目結(jié)構(gòu)(示意)
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, // 壓縮質(zhì)量(0-100)
Workers: 4, // 并發(fā) worker 數(shù)
Recursive: true, // 是否遞歸
}
if err := run(opts); err != nil {
fmt.Println("? 發(fā)生錯(cuò)誤:", 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.初始化項(xiàng)目并安裝依賴:
go mod init img-compressor go get github.com/chai2010/webp
2.放置圖片到 ./images 目錄。
3.運(yùn)行程序:
go run main.go
4.查看壓縮結(jié)果: 所有壓縮后的圖片會(huì)保存在 ./output 目錄下,目錄結(jié)構(gòu)保持不變。
實(shí)踐要點(diǎn)與注意事項(xiàng)
- 質(zhì)量參數(shù):JPEG 和 WebP 可以用
0-100來控制壓縮比;PNG 主要通過壓縮等級控制體積,但效果有限。 - 批量優(yōu)化:若圖片數(shù)量很多,可以用 goroutine worker pool 提升效率,避免一次性開太多協(xié)程。
- 格式兼容:WebP 在一些老舊系統(tǒng)和瀏覽器上兼容性不足,若是做網(wǎng)頁資源,需要配合 fallback。
- 壓縮策略:有時(shí)候先統(tǒng)一縮放圖片(比如長邊 1080px),再壓縮比單純調(diào)整質(zhì)量更省空間。
進(jìn)一步擴(kuò)展
- 添加 命令行參數(shù)(例如用
flag或cobra),讓用戶可以指定輸入目錄、輸出目錄、質(zhì)量、遞歸開關(guān)等。 - 支持 縮放功能(如最大寬高)。
- 輸出 統(tǒng)計(jì)信息(壓縮前后大小對比,總共節(jié)省空間)。
- 打包為跨平臺(tái)可執(zhí)行文件,方便非技術(shù)人員直接使用。
到此這篇關(guān)于Go語言開發(fā)實(shí)現(xiàn)一個(gè)圖片批量壓縮工具的文章就介紹到這了,更多相關(guān)Go圖片批量壓縮內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang遠(yuǎn)程調(diào)用框架RPC的具體使用
Remote Procedure Call (RPC) 是一種使用TCP協(xié)議從另一個(gè)系統(tǒng)調(diào)用應(yīng)用程序功能執(zhí)行的方法。Go有原生支持RPC服務(wù)器實(shí)現(xiàn),本文通過簡單實(shí)例介紹RPC的實(shí)現(xiàn)過程2022-12-12
基于go實(shí)例網(wǎng)絡(luò)存儲(chǔ)協(xié)議詳解
這篇文章主要為大家介紹了基于go實(shí)例網(wǎng)絡(luò)存儲(chǔ)協(xié)議詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
Go語言映射內(nèi)部實(shí)現(xiàn)及基礎(chǔ)功能實(shí)戰(zhàn)
這篇文章主要為大家介紹了Go語言映射的內(nèi)部實(shí)現(xiàn)和基礎(chǔ)功能實(shí)戰(zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2022-03-03
GoLang RabbitMQ TTL與死信隊(duì)列以及延遲隊(duì)列詳細(xì)講解
這篇文章主要介紹了GoLang RabbitMQ TTL與死信隊(duì)列以及延遲隊(duì)列,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-12-12
go語言LeetCode題解999可以被一步捕獲的棋子數(shù)
這篇文章主要為大家介紹了go語言LeetCode題解999可以被一步捕獲的棋子數(shù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Go語言實(shí)現(xiàn)單端口轉(zhuǎn)發(fā)到多個(gè)端口
這篇文章主要為大家詳細(xì)介紹了Go語言實(shí)現(xiàn)單端口轉(zhuǎn)發(fā)到多個(gè)端口,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,對大家的學(xué)習(xí)或工作有一定的幫助,需要的小伙伴可以了解下2024-02-02

