package handler import ( "fmt" "mime/multipart" "dianshang/internal/config" "dianshang/pkg/logger" "dianshang/pkg/response" "dianshang/pkg/storage" "github.com/gin-gonic/gin" ) type UploadHandler struct { config *config.Config localStorage *storage.LocalStorage ossStorage *storage.OSSStorage } func NewUploadHandler(cfg *config.Config) *UploadHandler { handler := &UploadHandler{ config: cfg, localStorage: storage.NewLocalStorage(&cfg.Upload), } // 如果配置了OSS,初始化OSS存储 if cfg.Upload.StorageType == "oss" { ossStorage, err := storage.NewOSSStorage(&cfg.Upload.OSS) if err != nil { logger.Errorf("初始化OSS存储失败: %v", err) logger.Warn("将使用本地存储作为后备") } else { handler.ossStorage = ossStorage logger.Info("OSS存储初始化成功") } } return handler } // UploadImage 上传图片 func (h *UploadHandler) UploadImage(c *gin.Context) { file, header, err := c.Request.FormFile("file") if err != nil { response.BadRequest(c, "获取上传文件失败: "+err.Error()) return } defer file.Close() // 验证文件类型 if !storage.IsValidImageType(header.Filename) { response.BadRequest(c, "不支持的图片格式,仅支持 jpg, jpeg, png, gif, webp, svg 格式") return } // 验证文件大小 (5MB) if header.Size > 5*1024*1024 { response.BadRequest(c, "文件过大,图片大小不能超过5MB") return } // 生成文件名 filename := storage.GenerateFilename(header.Filename) // 上传文件 fileURL, err := h.uploadFile(file, filename, "static/images") if err != nil { response.InternalServerError(c, "上传文件失败: "+err.Error()) return } response.Success(c, gin.H{ "url": fileURL, "filename": filename, "size": header.Size, }) } // UploadFile 上传通用文件 func (h *UploadHandler) UploadFile(c *gin.Context) { file, header, err := c.Request.FormFile("file") if err != nil { response.BadRequest(c, "获取上传文件失败: "+err.Error()) return } defer file.Close() // 验证文件大小 (10MB) if header.Size > 10*1024*1024 { response.BadRequest(c, "文件过大,文件大小不能超过10MB") return } // 生成文件名 filename := storage.GenerateFilename(header.Filename) // 上传文件 fileURL, err := h.uploadFile(file, filename, "static/uploads") if err != nil { response.InternalServerError(c, "上传文件失败: "+err.Error()) return } response.Success(c, gin.H{ "url": fileURL, "filename": filename, "size": header.Size, }) } // BatchUploadImages 批量上传图片 func (h *UploadHandler) BatchUploadImages(c *gin.Context) { form, err := c.MultipartForm() if err != nil { response.BadRequest(c, "获取上传文件失败: "+err.Error()) return } files := form.File["files"] if len(files) == 0 { response.BadRequest(c, "没有选择文件,请选择要上传的图片") return } // 限制批量上传数量 if len(files) > 10 { response.BadRequest(c, "文件数量过多,一次最多上传10个文件") return } var results []gin.H var errors []string for _, header := range files { file, err := header.Open() if err != nil { errors = append(errors, fmt.Sprintf("%s: 打开文件失败", header.Filename)) continue } // 验证文件类型 if !storage.IsValidImageType(header.Filename) { errors = append(errors, fmt.Sprintf("%s: 不支持的图片格式", header.Filename)) file.Close() continue } // 验证文件大小 if header.Size > 5*1024*1024 { errors = append(errors, fmt.Sprintf("%s: 文件过大", header.Filename)) file.Close() continue } // 生成文件名 filename := storage.GenerateFilename(header.Filename) // 上传文件 fileURL, err := h.uploadFile(file, filename, "static/images") file.Close() if err != nil { errors = append(errors, fmt.Sprintf("%s: 上传失败", header.Filename)) continue } // 添加成功结果 results = append(results, gin.H{ "url": fileURL, "filename": filename, "originalName": header.Filename, "size": header.Size, }) } response.Success(c, gin.H{ "files": results, "errors": errors, "total": len(files), "success": len(results), "failed": len(errors), }) } // uploadFile 核心上传方法,根据配置选择OSS或本地存储 func (h *UploadHandler) uploadFile(file multipart.File, filename string, objectPath string) (string, error) { // 如果配置为OSS且OSS存储已初始化,使用OSS if h.config.Upload.StorageType == "oss" && h.ossStorage != nil { fileURL, err := h.ossStorage.UploadFile(file, filename, objectPath) if err != nil { logger.Errorf("OSS上传失败: %v, 切换到本地存储", err) // OSS上传失败,降级到本地存储 return h.localStorage.UploadFile(file, filename, objectPath) } logger.Infof("文件上传到OSS成功: %s", fileURL) return fileURL, nil } // 使用本地存储 fileURL, err := h.localStorage.UploadFile(file, filename, objectPath) if err != nil { return "", err } logger.Infof("文件上传到本地成功: %s", fileURL) return fileURL, nil }