package probe import ( "fmt" "image" "os" "path/filepath" "strings" ) // supportImageTypes 认识的图片扩展名(大小写不敏感)。 // 这些格式可以自己读出尺寸;列表之外的(如 tiff)在 ffprobe 可用时兜底探测。 var supportImageTypes = []string{ ".bmp", ".gif", ".jpg", ".jpeg", ".jpe", ".png", ".webp", ".psb", ".jfif", } // IsSupportImage 判断扩展名是不是认识的图片格式。 func IsSupportImage(ext string) bool { for _, t := range supportImageTypes { if strings.EqualFold(t, ext) { return true } } return false } // ProbeImage 探测一张图片的尺寸(宽高)。 // // 流程(从快到慢、从准到兜底): // 1. 特殊格式(WebP/BMP/PSD)用专门写的读取函数,它们标准库不一定认识; // 2. 其余认识的格式用 Go 标准库读(最轻量); // 3. 上面都失败、或者格式不常见时,用 ffprobe 兜底(它认识的图片格式更多)。 // // ffprobePath 为空表示不启用兜底;遇到不认识的格式就报"不支持"。 func ProbeImage(filePath, ffprobePath string) (*ProbeResult, error) { ext := strings.ToLower(filepath.Ext(filePath)) // 先试试专门的读取函数 switch ext { case ".webp": if res, err := probeWebp(filePath); err == nil { return res, nil } case ".bmp": if res, err := probeBmp(filePath); err == nil { return res, nil } case ".psd", ".psb": if res, err := probePsd(filePath); err == nil { return res, nil } } // 不在认识列表里的格式:只能用 ffprobe 碰运气 if !IsSupportImage(ext) { if res, err := probeByFFprobe(ffprobePath, filePath); err == nil { return res, nil } return nil, fmt.Errorf("不支持的图片格式: %s", ext) } // 用标准库读宽高 if w, h, err := decodeConfig(filePath); err == nil { return &ProbeResult{Width: w, Height: h}, nil } // 标准库读不出 → 用 ffprobe 兜底 if res, err := probeByFFprobe(ffprobePath, filePath); err == nil { return res, nil } return nil, fmt.Errorf("无法读取图片尺寸: %s", filePath) } // probeByFFprobe 用 ffprobe 兜底读图片尺寸。 // ffprobePath 为空或探测失败时返回错误。 func probeByFFprobe(ffprobePath, filePath string) (*ProbeResult, error) { if ffprobePath == "" { return nil, fmt.Errorf("未启用 ffprobe 兜底") } // 图片在 ffprobe 眼里会被当成"只有视频流"的视频 if res, err := ProbeMediaFile(ffprobePath, filePath); err == nil && res.HasVideo { return res, nil } return nil, fmt.Errorf("ffprobe 兜底失败: %s", filePath) } // decodeConfig 用 Go 标准库读图片宽高(只解码头部配置,不加载整张图,很快)。 func decodeConfig(filePath string) (int, int, error) { f, err := os.Open(filePath) if err != nil { return 0, 0, err } defer f.Close() cfg, _, err := image.DecodeConfig(f) if err != nil { return 0, 0, err } return cfg.Width, cfg.Height, nil }