diff --git a/bin/main.go b/bin/main.go index 7e5fbf7..af2e8b6 100644 --- a/bin/main.go +++ b/bin/main.go @@ -1,20 +1,15 @@ package main import ( - "image" "log" "net/http" - "os" "runtime" "regexp" "strconv" "git.satori.love/gameui/webp/models" - "github.com/chai2010/webp" - "github.com/disintegration/imaging" _ "github.com/go-sql-driver/mysql" - giftowebp "github.com/sizeofint/gif-to-webp" ) func init() { @@ -39,64 +34,30 @@ func main() { var mysqlConnection models.MysqlConnection mysqlConnection.Init() + // URL 格式: /img/{type}/{id}.{format}?width=320&height=320&fit=cover http.HandleFunc("/img/", func(w http.ResponseWriter, r *http.Request) { log.Println(r.Method, r.URL.Path) - // URL 格式: /img/{type}/{id}.{format}?width=320&height=320&fit=cover reg := regexp.MustCompile(`^/img/([0-9a-zA-Z]+)/([0-9a-zA-Z]+).(jpg|jpeg|png|webp)$`) matches := reg.FindStringSubmatch(r.URL.Path) - if len(matches) != 5 { + if len(matches) != 4 { log.Println("URL 格式错误", matches) w.WriteHeader(http.StatusNotFound) return } - group, id, format, width, height, fit := matches[1], matches[2], matches[3], stringToInt(r.URL.Query().Get("width"), 0), stringToInt(r.URL.Query().Get("height"), 0), r.URL.Query().Get("fit") content, err := mysqlConnection.GetImageContent(group, id) if err != nil { log.Println("获取图片失败", format, err) - w.WriteHeader(http.StatusBadRequest) - } - - var img models.Image - img.Init(content) - - data, err := img.ToWebP(width, height, fit) - if err != nil { - log.Println("转换图片失败", err) - w.WriteHeader(http.StatusBadRequest) - } - - w.Header().Set("Content-Type", "image/webp") - w.Header().Set("Cache-Control", "max-age=31536000") - w.Write(data) - }) - - // 直接走 CDN 的请求不缓存本地, 因此直接使用参数 - http.HandleFunc("/webp/", func(w http.ResponseWriter, r *http.Request) { - log.Println(r.Method, r.URL.Path) - // URL 格式: /img/{id}.{格式}?width=320&height=320&fit=cover - reg := regexp.MustCompile(`^/webp/([0-9a-zA-Z]+).(jpg|jpeg|png|webp)$`) - matches := reg.FindStringSubmatch(r.URL.Path) - if len(matches) != 3 { - log.Println("URL 格式错误", matches) w.WriteHeader(http.StatusNotFound) return } - - id, format, width, height, fit := matches[1], matches[2], stringToInt(r.URL.Query().Get("width"), 0), stringToInt(r.URL.Query().Get("height"), 0), r.URL.Query().Get("fit") - content, err := mysqlConnection.GetImageContent(id) - if err != nil { - log.Println("获取图片失败", format, err) - w.WriteHeader(http.StatusBadRequest) - } - var img models.Image img.Init(content) - data, err := img.ToWebP(width, height, fit) if err != nil { log.Println("转换图片失败", err) w.WriteHeader(http.StatusBadRequest) + return } w.Header().Set("Content-Type", "image/webp") @@ -104,118 +65,6 @@ func main() { w.Write(data) }) - http.HandleFunc("/article/", func(w http.ResponseWriter, r *http.Request) { - // URL 格式: /article/{id}.{格式}?width=320&height=320&fit=cover - reg := regexp.MustCompile(`^/article/([0-9a-zA-Z]+).(jpg|jpeg|png|webp)$`) - matches := reg.FindStringSubmatch(r.URL.Path) - if len(matches) != 3 { - log.Println("URL 格式错误", matches) - w.WriteHeader(http.StatusNotFound) - return - } - - id, format, width, height, fit := matches[1], matches[2], stringToInt(r.URL.Query().Get("width"), 0), stringToInt(r.URL.Query().Get("height"), 0), r.URL.Query().Get("fit") - content, err := mysqlConnection.GetArticleImageContent(id) - if err != nil { - log.Println("获取图片失败", format, err) - w.WriteHeader(http.StatusBadRequest) - } - - var img models.Image - img.Init(content) - - data, err := img.ToWebP(width, height, fit) - if err != nil { - log.Println("转换图片失败", err) - w.WriteHeader(http.StatusBadRequest) - } - - w.Header().Set("Content-Type", "image/webp") - w.Header().Set("Cache-Control", "max-age=31536000") - w.Write(data) - }) - - 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) } diff --git a/models/mysql.go b/models/mysql.go index 8b8601e..4088f0f 100644 --- a/models/mysql.go +++ b/models/mysql.go @@ -2,6 +2,7 @@ package models import ( "database/sql" + "errors" "log" "strconv" @@ -42,6 +43,8 @@ func (m *MysqlConnection) GetImageContent(group string, id string) (content stri err = m.db.QueryRow("SELECT avatar FROM web_member WHERE id=" + id).Scan(&content) case "image": err = m.db.QueryRow("SELECT content FROM web_images WHERE id=" + id).Scan(&content) + default: + err = errors.New("group 参数错误") } return content, err }