Files
webp/bin/main.go
2023-04-07 02:54:59 +08:00

155 lines
4.5 KiB
Go

package main
import (
"database/sql"
"image"
"log"
"net/http"
"os"
"runtime"
"regexp"
"strconv"
"github.com/chai2010/webp"
"github.com/disintegration/imaging"
_ "github.com/go-sql-driver/mysql"
giftowebp "github.com/sizeofint/gif-to-webp"
"github.com/spf13/viper"
)
func init() {
// 设置日志格式
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
}
// 实现一个 web api 服务(获取指定尺寸的图片)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
// 读取配置文件yaml
viper.SetConfigName("config") // 设置配置文件的名字
viper.SetConfigType("yaml") // 设置配置文件的格式
viper.AddConfigPath("./data") // 设置配置文件所在的路径
if err := viper.ReadInConfig(); err != nil {
log.Println("读取配置文件失败", err)
return
}
user := viper.Get("mysql.user").(string)
password := viper.Get("mysql.password").(string)
host := viper.Get("mysql.host").(string)
port := viper.Get("mysql.port").(int)
database := viper.Get("mysql.database").(string)
sqlconf := user + ":" + password + "@tcp(" + host + ":" + strconv.Itoa(port) + ")/" + database + "?charset=utf8mb4&parseTime=True&loc=Local"
// 连接数据库
db, err := sql.Open("mysql", sqlconf)
if err != nil {
log.Println("连接数据库失败", err)
return
}
// 从 web_images 表读取图片 id, version, width, height, format
rows, err := db.Query("SELECT id, width, height, content FROM web_images WHERE id = 8888")
if err != nil {
log.Println("查询数据库失败", err)
return
}
defer rows.Close()
// 遍历查询结果
for rows.Next() {
var id, width, height, content string
if err = rows.Scan(&id, &width, &height, &content); err != nil {
log.Println("读取数据库失败", err)
return
}
log.Println(id, width, height, content)
}
http.HandleFunc("/img/", func(w http.ResponseWriter, r *http.Request) {
// URL 格式: /img/{id}-{version}@{倍图}{宽度}.{格式}
reg := regexp.MustCompile(`^/img/([0-9a-zA-Z]+)-([0-9a-zA-Z]+)@([0-9]{1,4})x([0-9]{1,4}).(jpg|jpeg|png|webp)$`)
matches := reg.FindStringSubmatch(r.URL.Path)
if len(matches) != 6 {
log.Println("URL 格式错误", matches)
w.WriteHeader(http.StatusBadRequest)
return
}
// 正则表达式获取参数
id, version, multiple_str, width_str, format := matches[1], matches[2], matches[3], matches[4], matches[5]
multiple, err := strconv.Atoi(multiple_str)
if err != nil {
log.Println("倍图参数错误", multiple_str)
w.WriteHeader(http.StatusBadRequest)
return
}
width, err := strconv.Atoi(width_str)
if err != nil {
log.Println("宽度参数错误", width_str)
w.WriteHeader(http.StatusBadRequest)
return
}
log.Println(id, version, multiple, width, format)
// 打開輸入文件
file, err := os.Open("data/test.gif")
if err != nil {
log.Println("打开文件失败", err)
w.WriteHeader(http.StatusBadRequest)
return
}
defer file.Close()
// 將輸入文件解碼為 image.Image
img, ext, err := image.Decode(file)
if err != nil {
log.Println("解码图像失败", err)
w.WriteHeader(http.StatusBadRequest)
return
}
// 如果是 GIF 格式的动态图片,直接返回
if ext == "gif" {
var gifBin, webpBuf []byte
if gifBin, err = os.ReadFile("data/test.gif"); err != nil {
log.Println("读取文件失败", err)
w.WriteHeader(http.StatusBadRequest)
return
}
converter := giftowebp.NewConverter()
converter.LoopCompatibility = false // 是否兼容循环动画
converter.WebPConfig.SetLossless(0) // 0 有损压缩 1无损压缩
converter.WebPConfig.SetMethod(6) // 压缩速度 0-6 0最快 6质量最好
converter.WebPConfig.SetQuality(10) // 压缩质量 0-100
converter.WebPAnimEncoderOptions.SetKmin(9)
converter.WebPAnimEncoderOptions.SetKmax(17)
if webpBuf, err = converter.Convert(gifBin); err != nil {
log.Println("编码图像失败", err.Error())
w.WriteHeader(http.StatusBadRequest)
return
}
w.Write(webpBuf)
return
}
// 將圖像轉換為 RGBA 格式(如果尚未採用該格式)
if _, ok := img.(*image.RGBA); !ok {
img = imaging.Clone(img)
}
// 將圖像調整為指定宽度的 WebP 格式並將其寫入輸出到瀏覽器(高度自适应)
if err = webp.Encode(w, imaging.Resize(img, multiple*width, 0, imaging.Lanczos), &webp.Options{Quality: 80}); err != nil {
log.Println("编码图像失败", err.Error())
w.WriteHeader(http.StatusBadRequest)
return
}
})
log.Println("Server is running at http://localhost:6001")
http.ListenAndServe(":6001", nil)
}