mirror of
https://github.com/woodchen-ink/webp_server_go.git
synced 2025-07-19 14:12:01 +08:00
refactor(handler): streamline file streaming and improve concurrency handling
This commit is contained in:
parent
85e0534523
commit
0a36dd5284
@ -3,7 +3,6 @@ package handler
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"mime"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
@ -126,37 +125,3 @@ func pingURL(url string) (string, int64, time.Time) {
|
|||||||
|
|
||||||
return etag, size, lastModified
|
return etag, size, lastModified
|
||||||
}
|
}
|
||||||
|
|
||||||
func streamFile(c *fiber.Ctx, filePath string) error {
|
|
||||||
file, err := os.Open(filePath)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("无法打开文件: %s, 错误: %v", filePath, err)
|
|
||||||
return c.Status(fiber.StatusInternalServerError).SendString("无法打开文件")
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
stat, err := file.Stat()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("无法获取文件信息: %s, 错误: %v", filePath, err)
|
|
||||||
return c.Status(fiber.StatusInternalServerError).SendString("无法获取文件信息")
|
|
||||||
}
|
|
||||||
|
|
||||||
contentType := mime.TypeByExtension(path.Ext(filePath))
|
|
||||||
if contentType == "" {
|
|
||||||
contentType = "application/octet-stream"
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Set(fiber.HeaderContentType, contentType)
|
|
||||||
c.Set(fiber.HeaderContentLength, strconv.FormatInt(stat.Size(), 10))
|
|
||||||
|
|
||||||
log.Infof("开始流式传输文件: %s, 大小: %d bytes", filePath, stat.Size())
|
|
||||||
|
|
||||||
err = c.SendStream(file)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("文件流式传输失败: %s, 错误: %v", filePath, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("文件流式传输完成: %s", filePath)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"webp_server_go/config"
|
"webp_server_go/config"
|
||||||
"webp_server_go/encoder"
|
"webp_server_go/encoder"
|
||||||
"webp_server_go/helper"
|
"webp_server_go/helper"
|
||||||
@ -62,7 +63,7 @@ func Convert(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 构建 EXHAUST_PATH 中的文件路径
|
// 构建 EXHAUST_PATH 中的文件路径
|
||||||
exhaustFilename := path.Join(config.Config.ExhaustPath, reqURI)
|
exhaustFilename := path.Join(config.Config.ExhaustPath, strings.TrimPrefix(reqURI, matchedPrefix))
|
||||||
if extraParams.Width > 0 || extraParams.Height > 0 || extraParams.MaxWidth > 0 || extraParams.MaxHeight > 0 {
|
if extraParams.Width > 0 || extraParams.Height > 0 || extraParams.MaxWidth > 0 || extraParams.MaxHeight > 0 {
|
||||||
ext := path.Ext(exhaustFilename)
|
ext := path.Ext(exhaustFilename)
|
||||||
extraParamsStr := fmt.Sprintf("_w%d_h%d_mw%d_mh%d", extraParams.Width, extraParams.Height, extraParams.MaxWidth, extraParams.MaxHeight)
|
extraParamsStr := fmt.Sprintf("_w%d_h%d_mw%d_mh%d", extraParams.Width, extraParams.Height, extraParams.MaxWidth, extraParams.MaxHeight)
|
||||||
@ -72,9 +73,14 @@ func Convert(c *fiber.Ctx) error {
|
|||||||
// 检查文件是否已经在 EXHAUST_PATH 中
|
// 检查文件是否已经在 EXHAUST_PATH 中
|
||||||
if helper.FileExists(exhaustFilename) {
|
if helper.FileExists(exhaustFilename) {
|
||||||
log.Infof("文件已存在于 EXHAUST_PATH,直接提供服务: %s", exhaustFilename)
|
log.Infof("文件已存在于 EXHAUST_PATH,直接提供服务: %s", exhaustFilename)
|
||||||
return streamFile(c, exhaustFilename)
|
return c.SendFile(exhaustFilename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 使用 sync.Once 确保并发安全
|
||||||
|
var once sync.Once
|
||||||
|
var processErr error
|
||||||
|
processImage := func() {
|
||||||
|
once.Do(func() {
|
||||||
// 文件不在 EXHAUST_PATH 中,需要处理
|
// 文件不在 EXHAUST_PATH 中,需要处理
|
||||||
isLocalPath := strings.HasPrefix(matchedTarget, "./") || strings.HasPrefix(matchedTarget, "/")
|
isLocalPath := strings.HasPrefix(matchedTarget, "./") || strings.HasPrefix(matchedTarget, "/")
|
||||||
var rawImageAbs string
|
var rawImageAbs string
|
||||||
@ -87,66 +93,80 @@ func Convert(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
// 检查本地文件是否存在
|
// 检查本地文件是否存在
|
||||||
if !helper.FileExists(rawImageAbs) {
|
if !helper.FileExists(rawImageAbs) {
|
||||||
log.Errorf("本地文件不存在: %s", rawImageAbs)
|
processErr = fmt.Errorf("本地文件不存在: %s", rawImageAbs)
|
||||||
return c.SendStatus(fiber.StatusNotFound)
|
return
|
||||||
}
|
}
|
||||||
isNewDownload = false // 本地文件不需要清理
|
isNewDownload = false // 本地文件不需要清理
|
||||||
} else {
|
} else {
|
||||||
// 处理远程URL
|
// 处理远程URL
|
||||||
targetUrl, err := url.Parse(matchedTarget)
|
targetUrl, err := url.Parse(matchedTarget)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("解析目标 URL 失败: %v", err)
|
processErr = fmt.Errorf("解析目标 URL 失败: %v", err)
|
||||||
return c.SendStatus(fiber.StatusInternalServerError)
|
return
|
||||||
}
|
}
|
||||||
remoteAddr := targetUrl.Scheme + "://" + targetUrl.Host + strings.Replace(reqURI, matchedPrefix, targetUrl.Path, 1)
|
remoteAddr := targetUrl.Scheme + "://" + targetUrl.Host + strings.TrimPrefix(reqURI, matchedPrefix)
|
||||||
|
|
||||||
rawImageAbs, isNewDownload, err = fetchRemoteImg(remoteAddr, targetUrl.Host)
|
rawImageAbs, isNewDownload, err = fetchRemoteImg(remoteAddr, targetUrl.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("获取远程图像失败: %v", err)
|
processErr = fmt.Errorf("获取远程图像失败: %v", err)
|
||||||
return c.SendStatus(fiber.StatusInternalServerError)
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否为允许的图片文件
|
// 检查是否为允许的图片文件
|
||||||
if !helper.IsAllowedImageFile(filename) {
|
if !helper.IsAllowedImageFile(filename) {
|
||||||
log.Infof("不允许的文件类型或非图片文件: %s", reqURI)
|
log.Infof("不允许的文件类型或非图片文件: %s", reqURI)
|
||||||
return streamFile(c, rawImageAbs)
|
// 直接复制文件到 EXHAUST_PATH
|
||||||
|
if err := helper.CopyFile(rawImageAbs, exhaustFilename); err != nil {
|
||||||
|
processErr = fmt.Errorf("复制不允许处理的文件失败: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理图片
|
// 处理图片
|
||||||
|
|
||||||
// 检查文件大小
|
|
||||||
isSmall, err := helper.IsFileSizeSmall(rawImageAbs, 100*1024) // 100KB
|
isSmall, err := helper.IsFileSizeSmall(rawImageAbs, 100*1024) // 100KB
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("检查文件大小时出错: %v", err)
|
processErr = fmt.Errorf("检查文件大小时出错: %v", err)
|
||||||
return c.SendStatus(fiber.StatusInternalServerError)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isSmall {
|
|
||||||
log.Infof("文件 %s 小于100KB,直接复制到 EXHAUST_PATH", rawImageAbs)
|
|
||||||
|
|
||||||
// 确保目标目录存在
|
// 确保目标目录存在
|
||||||
if err := os.MkdirAll(path.Dir(exhaustFilename), 0755); err != nil {
|
if err := os.MkdirAll(path.Dir(exhaustFilename), 0755); err != nil {
|
||||||
log.Errorf("创建目标目录失败: %v", err)
|
processErr = fmt.Errorf("创建目标目录失败: %v", err)
|
||||||
return c.SendStatus(fiber.StatusInternalServerError)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isSmall {
|
||||||
if err := helper.CopyFile(rawImageAbs, exhaustFilename); err != nil {
|
if err := helper.CopyFile(rawImageAbs, exhaustFilename); err != nil {
|
||||||
log.Errorf("复制小文件到 EXHAUST_PATH 失败: %v", err)
|
processErr = fmt.Errorf("复制小文件到 EXHAUST_PATH 失败: %v", err)
|
||||||
return c.SendStatus(fiber.StatusInternalServerError)
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 处理图片
|
if err := encoder.ProcessAndSaveImage(rawImageAbs, exhaustFilename, extraParams); err != nil {
|
||||||
err := encoder.ProcessAndSaveImage(rawImageAbs, exhaustFilename, extraParams)
|
processErr = fmt.Errorf("处理图片失败: %v", err)
|
||||||
if err != nil {
|
return
|
||||||
log.Errorf("处理图片失败: %v", err)
|
|
||||||
return c.SendStatus(fiber.StatusInternalServerError)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果是新下载的远程文件,安排清理任务
|
// 如果是新下载的远程文件,安排清理任务
|
||||||
if !isLocalPath && isNewDownload {
|
if !isLocalPath && isNewDownload {
|
||||||
go schedule.ScheduleCleanup(rawImageAbs)
|
go schedule.ScheduleCleanup(rawImageAbs)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
return streamFile(c, exhaustFilename)
|
}
|
||||||
|
|
||||||
|
// 处理图片
|
||||||
|
processImage()
|
||||||
|
if processErr != nil {
|
||||||
|
log.Error(processErr)
|
||||||
|
return c.Status(fiber.StatusInternalServerError).SendString(processErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再次检查文件是否存在(以防并发情况下的竞态条件)
|
||||||
|
if !helper.FileExists(exhaustFilename) {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).SendString("处理后的文件未找到")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送文件
|
||||||
|
return c.SendFile(exhaustFilename)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user