- 新增 imglib 包,支持 FilePath/URL/Base64 三种图片输入方式 - 纯白占比检测、白底居中合成、等比缩放、去白边、裁切 - 二维码生成与识别、条形码生成(Code128/EAN13/Code39) - 中文文字图片、书籍信息水印、通用水印叠加 - 输出辅助:EncodeToBytes/EncodeToBase64/SaveToFile/SaveJPEG/SavePNG - 字体缓存,避免重复加载 - 完整测试覆盖(23个测试用例)
124 lines
3.4 KiB
Go
124 lines
3.4 KiB
Go
package imglib
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/base64"
|
||
"fmt"
|
||
"image"
|
||
_ "image/jpeg"
|
||
_ "image/png"
|
||
"os"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/valyala/fasthttp"
|
||
)
|
||
|
||
// ImageInput 统一图片输入源,支持三种加载方式
|
||
type ImageInput struct {
|
||
FilePath string // 磁盘文件路径
|
||
URL string // 网络 URL(http/https)
|
||
Base64 string // Base64 编码的图片数据(可带 data:image/xxx;base64, 前缀)
|
||
}
|
||
|
||
// NewImageInputFromFile 从文件路径创建输入源
|
||
func NewImageInputFromFile(path string) ImageInput {
|
||
return ImageInput{FilePath: path}
|
||
}
|
||
|
||
// NewImageInputFromURL 从网络 URL 创建输入源
|
||
func NewImageInputFromURL(url string) ImageInput {
|
||
return ImageInput{URL: url}
|
||
}
|
||
|
||
// NewImageInputFromBase64 从 Base64 字符串创建输入源
|
||
func NewImageInputFromBase64(b64 string) ImageInput {
|
||
return ImageInput{Base64: b64}
|
||
}
|
||
|
||
// Load 加载图片,返回解码后的 image.Image 和格式名称
|
||
func (in ImageInput) Load() (image.Image, string, error) {
|
||
switch {
|
||
case in.Base64 != "":
|
||
return loadImageFromBase64(in.Base64)
|
||
case in.URL != "":
|
||
return loadImageFromURL(in.URL, 120)
|
||
case in.FilePath != "":
|
||
return loadImageFromFile(in.FilePath)
|
||
default:
|
||
return nil, "", fmt.Errorf("ImageInput 未设置任何输入源(FilePath/URL/Base64 均为空)")
|
||
}
|
||
}
|
||
|
||
// loadImageFromFile 从磁盘文件加载图片
|
||
func loadImageFromFile(path string) (image.Image, string, error) {
|
||
file, err := os.Open(path)
|
||
if err != nil {
|
||
return nil, "", fmt.Errorf("打开图片文件失败: %v", err)
|
||
}
|
||
defer file.Close()
|
||
|
||
img, format, err := image.Decode(file)
|
||
if err != nil {
|
||
return nil, "", fmt.Errorf("解码图片失败: %v", err)
|
||
}
|
||
return img, format, nil
|
||
}
|
||
|
||
// loadImageFromURL 从网络 URL 加载图片
|
||
func loadImageFromURL(url string, timeout int) (image.Image, string, error) {
|
||
if timeout <= 0 {
|
||
timeout = 120
|
||
}
|
||
|
||
req := fasthttp.AcquireRequest()
|
||
resp := fasthttp.AcquireResponse()
|
||
defer fasthttp.ReleaseRequest(req)
|
||
defer fasthttp.ReleaseResponse(resp)
|
||
|
||
req.SetRequestURI(url)
|
||
req.Header.SetMethod("GET")
|
||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
||
req.Header.Set("Accept", "image/avif,image/webp,image/apng,image/*,*/*;q=0.8")
|
||
|
||
client := &fasthttp.Client{
|
||
ReadTimeout: time.Duration(timeout) * time.Second,
|
||
WriteTimeout: time.Duration(timeout) * time.Second,
|
||
}
|
||
|
||
err := client.Do(req, resp)
|
||
if err != nil {
|
||
return nil, "", fmt.Errorf("请求失败: %v", err)
|
||
}
|
||
|
||
body := resp.Body()
|
||
imgData := make([]byte, len(body))
|
||
copy(imgData, body)
|
||
|
||
img, format, err := image.Decode(bytes.NewReader(imgData))
|
||
if err != nil {
|
||
return nil, "", fmt.Errorf("解码图片失败: %v", err)
|
||
}
|
||
return img, format, nil
|
||
}
|
||
|
||
// loadImageFromBase64 从 Base64 字符串加载图片
|
||
func loadImageFromBase64(b64 string) (image.Image, string, error) {
|
||
b64 = strings.TrimPrefix(b64, "data:image/jpeg;base64,")
|
||
b64 = strings.TrimPrefix(b64, "data:image/jpg;base64,")
|
||
b64 = strings.TrimPrefix(b64, "data:image/png;base64,")
|
||
b64 = strings.TrimPrefix(b64, "data:image/gif;base64,")
|
||
b64 = strings.TrimPrefix(b64, "data:image/webp;base64,")
|
||
|
||
imgData, err := base64.StdEncoding.DecodeString(b64)
|
||
if err != nil {
|
||
return nil, "", fmt.Errorf("解码 base64 失败: %v", err)
|
||
}
|
||
|
||
img, format, err := image.Decode(bytes.NewReader(imgData))
|
||
if err != nil {
|
||
return nil, "", fmt.Errorf("解码 base64 图片失败: %v", err)
|
||
}
|
||
return img, format, nil
|
||
}
|