207 lines
5.1 KiB
Go
207 lines
5.1 KiB
Go
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
|
||
} |