tss-rocks/backend/internal/handler/media.go
CDN 3e6181e578
All checks were successful
Build Backend / Build Docker Image (push) Successful in 5m3s
[feature/backend] overall enhancement of image uploading
2025-02-23 04:42:48 +08:00

271 lines
7.1 KiB
Go

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)
}