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/") { 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", "文件服务暂时不可用") } }