Files
hfb_sys/backend/internal/modules/file/handler.go
T

99 lines
2.5 KiB
Go

package file
import (
"errors"
"net/http"
"strings"
"hfb_sys/backend/pkg/response"
"github.com/gin-gonic/gin"
)
type Handler struct {
service *Service
storage *Storage
}
func NewHandler(service *Service, storage *Storage) *Handler {
return &Handler{service: service, storage: storage}
}
func (h *Handler) Upload(c *gin.Context) {
header, err := c.FormFile("file")
if err != nil {
response.BadRequest(c, "请选择要上传的文件")
return
}
reader, err := header.Open()
if err != nil {
response.BadRequest(c, "文件读取失败")
return
}
defer func() {
_ = reader.Close()
}()
item, err := h.service.Upload(uploadRequest{
Context: c.Request.Context(),
Scene: c.PostForm("scene"),
Header: header,
Reader: reader,
ContentType: header.Header.Get("Content-Type"),
})
if err != nil {
writeFileError(c, err)
return
}
response.Created(c, item)
}
func (h *Handler) Object(c *gin.Context) {
h.writeObject(c, false)
}
func (h *Handler) PublicObject(c *gin.Context) {
h.writeObject(c, true)
}
func (h *Handler) writeObject(c *gin.Context, publicOnly bool) {
if h.storage == nil {
response.ServiceUnavailable(c, "文件存储未连接")
return
}
key := strings.TrimSpace(c.Query("key"))
if key == "" || strings.Contains(key, "..") {
response.BadRequest(c, "文件 key 不正确")
return
}
if publicOnly && !strings.HasPrefix(key, "home-banner/") && !strings.HasPrefix(key, "avatar/") && !strings.HasPrefix(key, "payment-cert/") {
response.Error(c, http.StatusNotFound, "not_found", "文件不存在或暂不可访问")
return
}
object, err := h.storage.Get(c.Request.Context(), key)
if err != nil {
response.Error(c, http.StatusNotFound, "not_found", "文件不存在或暂不可访问")
return
}
defer func() {
_ = object.Reader.Close()
}()
contentType := object.ContentType
if contentType == "" {
contentType = "application/octet-stream"
}
c.Header("Content-Type", contentType)
c.Header("Cache-Control", "private, max-age=300")
c.DataFromReader(http.StatusOK, object.Size, contentType, object.Reader, nil)
}
func writeFileError(c *gin.Context, err error) {
switch {
case errors.Is(err, ErrDependencyUnavailable):
response.ServiceUnavailable(c, "文件存储未连接")
case errors.Is(err, ErrInvalidFile):
response.BadRequest(c, "文件不符合规则,仅支持 10MB 内的 JPG、PNG、WebP 或 PDF")
default:
response.Error(c, http.StatusInternalServerError, "internal_error", "文件服务暂时不可用")
}
}