mirror of
https://github.com/woodchen-ink/webp_server_go.git
synced 2025-07-18 13:42:02 +08:00
feat(config, handler): Remove IMG_PATH config and update IMG_MAP handling, add root path greeting
This commit is contained in:
parent
c8f3d0bf38
commit
d872d71dcb
@ -31,6 +31,8 @@
|
||||
* 使用流式JSON编码器
|
||||
* 内存使用监控
|
||||
6. 根目录设置打招呼
|
||||
7. 移除`IMG_PATH`的配置方式, 只保留`IMG_MAP`.
|
||||
8. 当前缀与`IMG_MAP`不匹配时, 直接返回错误
|
||||
|
||||
<p align="center">
|
||||
<img src="./pics/webp_server.png"/>
|
||||
|
@ -137,7 +137,7 @@ func LoadConfig() {
|
||||
decoder := json.NewDecoder(jsonObject)
|
||||
_ = decoder.Decode(&Config)
|
||||
_ = jsonObject.Close()
|
||||
switchProxyMode()
|
||||
|
||||
Config.ImageMap = parseImgMap(Config.ImageMap)
|
||||
|
||||
if slices.Contains(Config.ConvertTypes, "webp") {
|
||||
@ -282,12 +282,12 @@ func parseImgMap(imgMap map[string]string) map[string]string {
|
||||
var parsedImgMap = map[string]string{}
|
||||
httpRegexpMatcher := regexp.MustCompile(HttpRegexp)
|
||||
for uriMap, uriMapTarget := range imgMap {
|
||||
if httpRegexpMatcher.Match([]byte(uriMap)) || strings.HasPrefix(uriMap, "/") {
|
||||
if httpRegexpMatcher.Match([]byte(uriMapTarget)) || strings.HasPrefix(uriMap, "/") {
|
||||
// Valid
|
||||
parsedImgMap[uriMap] = uriMapTarget
|
||||
} else {
|
||||
// Invalid
|
||||
log.Warnf("IMG_MAP key '%s' does matches '%s' or starts with '/' - skipped", uriMap, HttpRegexp)
|
||||
log.Warnf("IMG_MAP 值'%s'与'%s'不匹配或键不以“/”开头 -已跳过", uriMapTarget, HttpRegexp)
|
||||
}
|
||||
}
|
||||
return parsedImgMap
|
||||
@ -299,12 +299,3 @@ type ExtraParams struct {
|
||||
MaxWidth int // in px
|
||||
MaxHeight int // in px
|
||||
}
|
||||
|
||||
func switchProxyMode() {
|
||||
matched, _ := regexp.MatchString(HttpRegexp, Config.ImgPath)
|
||||
if matched {
|
||||
// Enable proxy based on ImgPath should be deprecated in future versions
|
||||
log.Warn("Enable proxy based on ImgPath will be deprecated in future versions. Use IMG_MAP config options instead")
|
||||
ProxyMode = true
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"webp_server_go/config"
|
||||
@ -16,43 +15,29 @@ import (
|
||||
)
|
||||
|
||||
func Convert(c *fiber.Ctx) error {
|
||||
// this function need to do:
|
||||
// 1. get request path, query string
|
||||
// 2. generate rawImagePath, could be local path or remote url(possible with query string)
|
||||
// 3. pass it to encoder, get the result, send it back
|
||||
|
||||
// normal http request will start with /
|
||||
// 检查路径是否以 "/" 开头
|
||||
if !strings.HasPrefix(c.Path(), "/") {
|
||||
return c.SendStatus(http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// 处理根路径请求
|
||||
// 检查是否为根路径
|
||||
if c.Path() == "/" {
|
||||
return c.SendString("Welcome to CZL WebP Server")
|
||||
}
|
||||
|
||||
var (
|
||||
reqHostname = c.Hostname()
|
||||
reqHost = c.Protocol() + "://" + reqHostname // http://www.example.com:8000
|
||||
reqHeader = &c.Request().Header
|
||||
|
||||
reqURIRaw, _ = url.QueryUnescape(c.Path()) // /mypic/123.jpg
|
||||
reqURIwithQueryRaw, _ = url.QueryUnescape(c.OriginalURL()) // /mypic/123.jpg?someother=200&somebugs=200
|
||||
reqURI = path.Clean(reqURIRaw) // delete ../ in reqURI to mitigate directory traversal
|
||||
reqURIwithQuery = path.Clean(reqURIwithQueryRaw) // Sometimes reqURIwithQuery can be https://example.tld/mypic/123.jpg?someother=200&somebugs=200, we need to extract it
|
||||
reqURIRaw, _ = url.QueryUnescape(c.Path())
|
||||
reqURIwithQueryRaw, _ = url.QueryUnescape(c.OriginalURL())
|
||||
reqURI = path.Clean(reqURIRaw)
|
||||
reqURIwithQuery = path.Clean(reqURIwithQueryRaw)
|
||||
|
||||
filename = path.Base(reqURI)
|
||||
realRemoteAddr = ""
|
||||
targetHostName = config.LocalHostAlias
|
||||
targetHost = config.Config.ImgPath
|
||||
proxyMode = config.ProxyMode
|
||||
mapMode = false
|
||||
targetHostName = ""
|
||||
targetHost = ""
|
||||
|
||||
width, _ = strconv.Atoi(c.Query("width")) // Extra Params
|
||||
height, _ = strconv.Atoi(c.Query("height")) // Extra Params
|
||||
maxHeight, _ = strconv.Atoi(c.Query("max_height")) // Extra Params
|
||||
maxWidth, _ = strconv.Atoi(c.Query("max_width")) // Extra Params
|
||||
width, _ = strconv.Atoi(c.Query("width"))
|
||||
height, _ = strconv.Atoi(c.Query("height"))
|
||||
maxHeight, _ = strconv.Atoi(c.Query("max_height"))
|
||||
maxWidth, _ = strconv.Atoi(c.Query("max_width"))
|
||||
extraParams = config.ExtraParams{
|
||||
Width: width,
|
||||
Height: height,
|
||||
@ -63,155 +48,65 @@ func Convert(c *fiber.Ctx) error {
|
||||
|
||||
log.Debugf("Incoming connection from %s %s %s", c.IP(), reqHostname, reqURIwithQuery)
|
||||
|
||||
var rawImageAbs string
|
||||
var metadata = config.MetaFile{}
|
||||
|
||||
// 非图片清况下302到源文件
|
||||
if !helper.IsImageFile(filename) {
|
||||
log.Infof("Non-image file requested: %s", reqURI)
|
||||
var redirectURL string
|
||||
|
||||
// 检查是否存在匹配的 IMG_MAP
|
||||
// 检查路径是否匹配 IMG_MAP 中的任何前缀
|
||||
var matchedPrefix string
|
||||
var matchedTarget string
|
||||
for prefix, target := range config.Config.ImageMap {
|
||||
if strings.HasPrefix(reqURI, prefix) {
|
||||
// 检查目标是否为远程资源
|
||||
if strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://") {
|
||||
// 远程资源,构造重定向 URL
|
||||
redirectURL = target + strings.TrimPrefix(reqURI, prefix)
|
||||
} else {
|
||||
// 本地资源,按原逻辑处理
|
||||
return c.SendFile(path.Join(target, strings.TrimPrefix(reqURI, prefix)))
|
||||
}
|
||||
matchedPrefix = prefix
|
||||
matchedTarget = target
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到匹配的 IMG_MAP,或者是本地资源,使用默认的处理方式
|
||||
if redirectURL == "" {
|
||||
if proxyMode {
|
||||
redirectURL = realRemoteAddr
|
||||
} else {
|
||||
// 本地资源,按原逻辑处理
|
||||
localPath := path.Join(config.Config.ImgPath, reqURI)
|
||||
if helper.FileExists(localPath) {
|
||||
return c.SendFile(localPath)
|
||||
} else {
|
||||
// 如果不匹配任何 IMG_MAP 前缀,直接返回 404
|
||||
if matchedPrefix == "" {
|
||||
log.Warnf("请求的路径不匹配任何配置的 IMG_MAP: %s", c.Path())
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 只有在确定需要重定向时才执行重定向
|
||||
if redirectURL != "" {
|
||||
log.Infof("Redirecting to: %s", redirectURL)
|
||||
return c.Redirect(redirectURL, fiber.StatusFound)
|
||||
}
|
||||
|
||||
// 设置目标主机信息
|
||||
targetUrl, _ := url.Parse(matchedTarget)
|
||||
targetHostName = targetUrl.Host
|
||||
targetHost = targetUrl.Scheme + "://" + targetUrl.Host
|
||||
|
||||
// 调整请求 URI
|
||||
reqURI = strings.Replace(reqURI, matchedPrefix, targetUrl.Path, 1)
|
||||
reqURIwithQuery = strings.Replace(reqURIwithQuery, matchedPrefix, targetUrl.Path, 1)
|
||||
|
||||
// 构造远程地址
|
||||
realRemoteAddr = targetHost + reqURIwithQuery
|
||||
|
||||
// 处理非图片文件
|
||||
if !helper.IsImageFile(filename) {
|
||||
log.Infof("Non-image file requested: %s", reqURI)
|
||||
log.Infof("Redirecting to: %s", realRemoteAddr)
|
||||
return c.Redirect(realRemoteAddr, fiber.StatusFound)
|
||||
}
|
||||
|
||||
// 检查允许的文件类型
|
||||
if !helper.CheckAllowedType(filename) {
|
||||
msg := "不允许的文件扩展名 " + filename
|
||||
log.Warn(msg)
|
||||
c.Status(http.StatusBadRequest)
|
||||
_ = c.Send([]byte(msg))
|
||||
return nil
|
||||
return c.Status(http.StatusBadRequest).SendString(msg)
|
||||
}
|
||||
|
||||
// Rewrite the target backend if a mapping rule matches the hostname
|
||||
if hostMap, hostMapFound := config.Config.ImageMap[reqHost]; hostMapFound {
|
||||
log.Debugf("找到host映射 %s -> %s", reqHostname, hostMap)
|
||||
targetHostUrl, _ := url.Parse(hostMap)
|
||||
targetHostName = targetHostUrl.Host
|
||||
targetHost = targetHostUrl.Scheme + "://" + targetHostUrl.Host
|
||||
proxyMode = true
|
||||
} else {
|
||||
// There's not matching host mapping, now check for any URI map that apply
|
||||
httpRegexpMatcher := regexp.MustCompile(config.HttpRegexp)
|
||||
for uriMap, uriMapTarget := range config.Config.ImageMap {
|
||||
if strings.HasPrefix(reqURI, uriMap) {
|
||||
log.Debugf("找到 URI 映射 %s -> %s", uriMap, uriMapTarget)
|
||||
mapMode = true
|
||||
|
||||
// if uriMapTarget we use the proxy mode to fetch the remote
|
||||
if httpRegexpMatcher.Match([]byte(uriMapTarget)) {
|
||||
targetHostUrl, _ := url.Parse(uriMapTarget)
|
||||
targetHostName = targetHostUrl.Host
|
||||
targetHost = targetHostUrl.Scheme + "://" + targetHostUrl.Host
|
||||
reqURI = strings.Replace(reqURI, uriMap, targetHostUrl.Path, 1)
|
||||
reqURIwithQuery = strings.Replace(reqURIwithQuery, uriMap, targetHostUrl.Path, 1)
|
||||
proxyMode = true
|
||||
} else {
|
||||
reqURI = strings.Replace(reqURI, uriMap, uriMapTarget, 1)
|
||||
reqURIwithQuery = strings.Replace(reqURIwithQuery, uriMap, uriMapTarget, 1)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if proxyMode {
|
||||
|
||||
if !mapMode {
|
||||
// Don't deal with the encoding to avoid upstream compatibilities
|
||||
reqURI = c.Path()
|
||||
reqURIwithQuery = c.OriginalURL()
|
||||
}
|
||||
|
||||
log.Tracef("reqURIwithQuery is %s", reqURIwithQuery)
|
||||
|
||||
// Replace host in the URL
|
||||
// realRemoteAddr = strings.Replace(reqURIwithQuery, reqHost, targetHost, 1)
|
||||
realRemoteAddr = targetHost + reqURIwithQuery
|
||||
log.Debugf("realRemoteAddr is %s", realRemoteAddr)
|
||||
}
|
||||
|
||||
if proxyMode {
|
||||
// 这是 proxyMode,我们必须使用这个 url 来下载并将其保存到本地路径,这也为我们提供了 rawImageAbs
|
||||
// https://test.webp.sh/mypic/123.jpg?someother=200&somebugs=200
|
||||
|
||||
metadata = fetchRemoteImg(realRemoteAddr, targetHostName)
|
||||
rawImageAbs = path.Join(config.Config.RemoteRawPath, targetHostName, metadata.Id)
|
||||
} else {
|
||||
// not proxyMode, we'll use local path
|
||||
metadata = helper.ReadMetadata(reqURIwithQuery, "", targetHostName)
|
||||
if !mapMode {
|
||||
// by default images are hosted in ImgPath
|
||||
rawImageAbs = path.Join(config.Config.ImgPath, reqURI)
|
||||
} else {
|
||||
rawImageAbs = reqURI
|
||||
}
|
||||
// detect if source file has changed
|
||||
if metadata.Checksum != helper.HashFile(rawImageAbs) {
|
||||
log.Info("源文件已更改,重新编码...")
|
||||
helper.WriteMetadata(reqURIwithQuery, "", targetHostName)
|
||||
cleanProxyCache(path.Join(config.Config.ExhaustPath, targetHostName, metadata.Id))
|
||||
}
|
||||
}
|
||||
// 获取远程图像元数据
|
||||
metadata := fetchRemoteImg(realRemoteAddr, targetHostName)
|
||||
rawImageAbs := path.Join(config.Config.RemoteRawPath, targetHostName, metadata.Id)
|
||||
|
||||
// 后续的图像处理逻辑
|
||||
supportedFormats := helper.GuessSupportedFormat(reqHeader)
|
||||
// resize itself and return if only raw(original format) is supported
|
||||
if supportedFormats["raw"] == true &&
|
||||
supportedFormats["webp"] == false &&
|
||||
supportedFormats["avif"] == false &&
|
||||
supportedFormats["jxl"] == false {
|
||||
dest := path.Join(config.Config.ExhaustPath, targetHostName, metadata.Id)
|
||||
if !helper.ImageExists(dest) {
|
||||
encoder.ResizeItself(rawImageAbs, dest, extraParams)
|
||||
}
|
||||
return c.SendFile(dest)
|
||||
}
|
||||
|
||||
// 检查原始图像是否存在,
|
||||
// 检查原始图像是否存在
|
||||
if !helper.ImageExists(rawImageAbs) {
|
||||
helper.DeleteMetadata(reqURIwithQuery, targetHostName)
|
||||
msg := "Image not found!"
|
||||
_ = c.Send([]byte(msg))
|
||||
log.Warn(msg)
|
||||
_ = c.SendStatus(404)
|
||||
return nil
|
||||
return c.Status(404).SendString(msg)
|
||||
}
|
||||
|
||||
// 新增:检查文件大小
|
||||
// 检查文件大小
|
||||
isSmall, err := helper.IsFileSizeSmall(rawImageAbs, 100*1024) // 100KB
|
||||
if err != nil {
|
||||
log.Errorf("检查文件大小时出错: %v", err)
|
||||
@ -224,7 +119,7 @@ func Convert(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
avifAbs, webpAbs, jxlAbs := helper.GenOptimizedAbsPath(metadata, targetHostName)
|
||||
// Do the convertion based on supported formats and config
|
||||
// 根据支持的格式和配置进行转换
|
||||
encoder.ConvertFilter(rawImageAbs, jxlAbs, avifAbs, webpAbs, extraParams, supportedFormats, nil)
|
||||
|
||||
var availableFiles = []string{rawImageAbs}
|
||||
|
Loading…
x
Reference in New Issue
Block a user