spark-server/cmd/compress-images/main.go
DESKTOP-L8OUL9F\gfl0034 753c276496 feat: PopAdmin 拉取管理、签到能量 API 与图片 150KiB 硬上限
管理端按用户查询开启通讯录/相册/媒体拉取;服务端写入路径自动压图;新增 /v1/sign 与 /v1/energy。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-02 12:44:06 +08:00

139 lines
3.4 KiB
Go

// Command compress-images scans a directory for raster images over max-bytes
// and rewrites them as JPEG ≤ limit, keeping a .bak sibling of the original.
//
// go run ./cmd/compress-images -dir ./uploads -max-bytes 153600
// go run ./cmd/compress-images -dir ./uploads -dry-run
//
// Safe for local/mounted dirs. Does not wipe remote stores or touch admin JWT.
package main
import (
"flag"
"fmt"
"os"
"path/filepath"
"strings"
infmedia "spark-server/internal/infrastructure/media"
pkgmedia "spark-server/internal/pkg/media"
)
func main() {
dir := flag.String("dir", "", "directory to scan (required)")
maxBytes := flag.Int64("max-bytes", pkgmedia.DefaultImageMaxBytes, "target max bytes (default 150KiB)")
dryRun := flag.Bool("dry-run", false, "only report oversized files")
flag.Parse()
if strings.TrimSpace(*dir) == "" {
fmt.Fprintln(os.Stderr, "usage: compress-images -dir PATH [-max-bytes 153600] [-dry-run]")
os.Exit(2)
}
opt := &infmedia.Options{
Limits: pkgmedia.Limits{
ImageMaxEdge: pkgmedia.DefaultImageMaxEdge,
ImageMaxBytes: *maxBytes,
}.Normalize(),
JPEGQuality: pkgmedia.DefaultJPEGQuality,
}
var scanned, oversized, rewritten, failed int
err := filepath.WalkDir(*dir, func(path string, d os.DirEntry, err error) error {
if err != nil {
return nil
}
if d.IsDir() {
return nil
}
name := strings.ToLower(d.Name())
if strings.HasSuffix(name, ".bak") {
return nil
}
mime := mimeFromExt(name)
if mime == "" {
return nil
}
scanned++
info, err := d.Info()
if err != nil {
return nil
}
if info.Size() <= *maxBytes {
return nil
}
oversized++
fmt.Printf("OVER %s (%d bytes)\n", path, info.Size())
if *dryRun {
return nil
}
raw, err := os.ReadFile(path)
if err != nil {
failed++
fmt.Printf(" read err: %v\n", err)
return nil
}
out, outMIME, err := infmedia.EnsureUnderLimit(raw, mime, opt)
if err != nil {
failed++
fmt.Printf(" compress err: %v\n", err)
return nil
}
if int64(len(out)) >= info.Size() && int64(len(out)) > *maxBytes {
failed++
fmt.Printf(" still oversized (%d)\n", len(out))
return nil
}
bak := path + ".bak"
if err := os.WriteFile(bak, raw, 0o644); err != nil {
failed++
fmt.Printf(" bak err: %v\n", err)
return nil
}
target := path
if outMIME == "image/jpeg" && !hasJPEGExt(name) {
target = stripExt(path) + ".jpg"
}
if err := os.WriteFile(target, out, 0o644); err != nil {
failed++
fmt.Printf(" write err: %v\n", err)
return nil
}
if target != path {
_ = os.Remove(path)
}
rewritten++
fmt.Printf(" OK %d -> %d bytes (%s)\n", info.Size(), len(out), target)
return nil
})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Printf("done scanned=%d oversized=%d rewritten=%d failed=%d dry-run=%v\n",
scanned, oversized, rewritten, failed, *dryRun)
}
func mimeFromExt(name string) string {
switch {
case strings.HasSuffix(name, ".jpg"), strings.HasSuffix(name, ".jpeg"):
return "image/jpeg"
case strings.HasSuffix(name, ".png"):
return "image/png"
case strings.HasSuffix(name, ".gif"):
return "image/gif"
case strings.HasSuffix(name, ".webp"):
return "image/webp"
default:
return ""
}
}
func hasJPEGExt(name string) bool {
return strings.HasSuffix(name, ".jpg") || strings.HasSuffix(name, ".jpeg")
}
func stripExt(path string) string {
ext := filepath.Ext(path)
if ext == "" {
return path
}
return strings.TrimSuffix(path, ext)
}