package handler import ( "fmt" "io" "net/http" "strconv" "strings" "github.com/gin-gonic/gin" "github.com/rs/zerolog/log" "path/filepath" ) // Media handlers func (h *Handler) ListMedia(c *gin.Context) { limit := 10 // Default limit if limitStr := c.Query("limit"); limitStr != "" { if l, err := strconv.Atoi(limitStr); err == nil && l > 0 { limit = l } } offset := 0 // Default offset if offsetStr := c.Query("offset"); offsetStr != "" { if o, err := strconv.Atoi(offsetStr); err == nil && o >= 0 { offset = o } } media, err := h.service.ListMedia(c.Request.Context(), limit, offset) if err != nil { log.Error().Err(err).Msg("Failed to list media") c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list media"}) return } c.JSON(http.StatusOK, gin.H{ "data": media, }) } func (h *Handler) UploadMedia(c *gin.Context) { // Get user ID from context (set by auth middleware) userIDStr, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"}) return } // Convert user ID to int userID, err := strconv.Atoi(userIDStr.(string)) if err != nil { log.Error().Err(err). Str("user_id", fmt.Sprintf("%v", userIDStr)). Msg("Failed to convert user ID to int") c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"}) return } // Get file from form file, err := c.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "No file uploaded"}) return } // 获取文件类型和扩展名 contentType := file.Header.Get("Content-Type") ext := strings.ToLower(filepath.Ext(file.Filename)) if contentType == "" { // 如果 Content-Type 为空,尝试从文件扩展名判断 switch ext { case ".jpg", ".jpeg": contentType = "image/jpeg" case ".png": contentType = "image/png" case ".gif": contentType = "image/gif" case ".webp": contentType = "image/webp" case ".mp4": contentType = "video/mp4" case ".webm": contentType = "video/webm" case ".mp3": contentType = "audio/mpeg" case ".ogg": contentType = "audio/ogg" case ".wav": contentType = "audio/wav" case ".pdf": contentType = "application/pdf" case ".doc": contentType = "application/msword" case ".docx": contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document" } } // 根据 Content-Type 确定文件类型和限制 var maxSize int64 var allowedTypes []string var fileType string limits := h.cfg.Storage.Upload.Limits switch { case strings.HasPrefix(contentType, "image/"): maxSize = int64(limits.Image.MaxSize) * 1024 * 1024 allowedTypes = limits.Image.AllowedTypes fileType = "image" case strings.HasPrefix(contentType, "video/"): maxSize = int64(limits.Video.MaxSize) * 1024 * 1024 allowedTypes = limits.Video.AllowedTypes fileType = "video" case strings.HasPrefix(contentType, "audio/"): maxSize = int64(limits.Audio.MaxSize) * 1024 * 1024 allowedTypes = limits.Audio.AllowedTypes fileType = "audio" case strings.HasPrefix(contentType, "application/"): maxSize = int64(limits.Document.MaxSize) * 1024 * 1024 allowedTypes = limits.Document.AllowedTypes fileType = "document" default: c.JSON(http.StatusBadRequest, gin.H{ "error": "Unsupported file type", }) return } // 检查文件类型是否允许 typeAllowed := false for _, allowed := range allowedTypes { if contentType == allowed { typeAllowed = true break } } if !typeAllowed { c.JSON(http.StatusBadRequest, gin.H{ "error": fmt.Sprintf("Unsupported %s type: %s", fileType, contentType), }) return } // 检查文件大小 if file.Size > maxSize { c.JSON(http.StatusBadRequest, gin.H{ "error": fmt.Sprintf("File size exceeds the limit (%d MB) for %s files", limits.Image.MaxSize, fileType), }) return } // Upload file media, err := h.service.Upload(c.Request.Context(), file, userID) if err != nil { log.Error().Err(err). Str("filename", file.Filename). Str("content_type", contentType). Int("user_id", userID). Msg("Failed to upload media") c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to upload media"}) return } c.JSON(http.StatusCreated, gin.H{ "data": media, }) } func (h *Handler) GetMedia(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid media ID"}) return } // Get media metadata media, err := h.service.GetMedia(c.Request.Context(), id) if err != nil { log.Error().Err(err).Msg("Failed to get media") c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get media"}) return } // Get file content reader, info, err := h.service.GetFile(c.Request.Context(), media.StorageID) if err != nil { log.Error().Err(err).Msg("Failed to get media file") c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get media file"}) return } defer reader.Close() // Set response headers c.Header("Content-Type", media.MimeType) c.Header("Content-Length", fmt.Sprintf("%d", info.Size)) c.Header("Content-Disposition", fmt.Sprintf("inline; filename=%s", media.OriginalName)) // Stream the file if _, err := io.Copy(c.Writer, reader); err != nil { log.Error().Err(err).Msg("Failed to stream media file") return } } func (h *Handler) GetMediaFile(c *gin.Context) { year := c.Param("year") month := c.Param("month") filename := c.Param("filename") // Get file content reader, info, err := h.service.GetFile(c.Request.Context(), filename) // 直接使用完整的文件名 if err != nil { log.Error().Err(err). Str("year", year). Str("month", month). Str("filename", filename). Msg("Failed to get media file") c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get media file"}) return } defer reader.Close() // Set response headers c.Header("Content-Type", info.ContentType) c.Header("Content-Length", fmt.Sprintf("%d", info.Size)) c.Header("Content-Disposition", fmt.Sprintf("inline; filename=%s", info.Name)) // Stream the file if _, err := io.Copy(c.Writer, reader); err != nil { log.Error().Err(err).Msg("Failed to stream media file") return } } func (h *Handler) DeleteMedia(c *gin.Context) { // Get user ID from context (set by auth middleware) userIDStr, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"}) return } // Convert user ID to int userID, err := strconv.Atoi(userIDStr.(string)) if err != nil { log.Error().Err(err). Str("user_id", fmt.Sprintf("%v", userIDStr)). Msg("Failed to convert user ID to int") c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"}) return } id, err := strconv.Atoi(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid media ID"}) return } if err := h.service.DeleteMedia(c.Request.Context(), id, userID); err != nil { log.Error().Err(err). Int("media_id", id). Int("user_id", userID). Msg("Failed to delete media") c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete media"}) return } c.JSON(http.StatusNoContent, nil) }