Files

131 lines
3.5 KiB
Go
Raw Permalink Normal View History

2026-06-26 14:31:24 +08:00
// Package handlers 实现文件上传相关的API请求处理器
// 支持图片、视频、音频、文档等文件上传
package handlers
import (
"excel-api/config"
"excel-api/utils"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/gin-gonic/gin"
)
// allowedExtensions 允许上传的文件扩展名白名单
var allowedExtensions = map[string]bool{
// 图片格式
".png": true, ".jpg": true, ".jpeg": true, ".gif": true, ".webp": true, ".svg": true,
// 视频格式
".mp4": true, ".webm": true, ".mov": true,
// 音频格式
".mp3": true, ".wav": true, ".ogg": true, ".flac": true,
// 压缩包格式
".zip": true, ".rar": true, ".7z": true, ".tar": true, ".gz": true,
// 文档格式
".pdf": true, ".doc": true, ".docx": true, ".xls": true, ".xlsx": true,
".ppt": true, ".pptx": true, ".txt": true, ".csv": true,
}
// DetectFileType 根据文件扩展名自动检测文件类型
// 返回值: image/video/audio/archive/file
func DetectFileType(filename string) string {
ext := strings.ToLower(filepath.Ext(filename))
switch ext {
case ".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg":
return "image"
case ".mp4", ".webm", ".mov":
return "video"
case ".mp3", ".wav", ".ogg", ".flac":
return "audio"
case ".zip", ".rar", ".7z", ".tar", ".gz":
return "archive"
default:
return "file"
}
}
// UploadResponse 文件上传响应结构体
type UploadResponse struct {
URL string `json:"url"` // 文件访问URL
Name string `json:"name"` // 原始文件名
Size int64 `json:"size"` // 文件大小(字节)
MimeType string `json:"mimeType"` // 文件MIME类型
FileType string `json:"fileType"` // 自动检测的文件类型
}
// Upload 处理文件上传请求
// 请求方式: POST
// 路径: /api/upload
// 请求体: multipart/form-data字段名为 "file"
func Upload(c *gin.Context) {
// 获取上传的文件
file, err := c.FormFile("file")
if err != nil {
utils.Error(c, 400, "请选择要上传的文件")
return
}
// 验证文件扩展名
ext := strings.ToLower(filepath.Ext(file.Filename))
if !allowedExtensions[ext] {
utils.Error(c, 400, "不支持的文件格式: "+ext)
return
}
// 限制文件大小(最大 100MB
const maxFileSize = 100 * 1024 * 1024
if file.Size > maxFileSize {
utils.Error(c, 400, "文件大小超过限制最大100MB")
return
}
// 确定存储路径
storagePath := config.App.Storage.Local.Path
if storagePath == "" {
storagePath = "./uploads"
}
// 按日期创建子目录uploads/2024/01/
dateDir := time.Now().Format("2006/01")
uploadDir := filepath.Join(storagePath, dateDir)
// 创建存储目录(如果不存在)
if err := os.MkdirAll(uploadDir, 0755); err != nil {
utils.Error(c, 500, "创建上传目录失败")
return
}
// 生成唯一文件名:时间戳 + 随机数 + 原始扩展名
filename := fmt.Sprintf("%d%s", time.Now().UnixNano(), ext)
filePath := filepath.Join(uploadDir, filename)
// 保存文件到磁盘
if err := c.SaveUploadedFile(file, filePath); err != nil {
utils.Error(c, 500, "保存文件失败")
return
}
// 检测文件MIME类型
mimeType := file.Header.Get("Content-Type")
if mimeType == "" {
mimeType = "application/octet-stream"
}
// 自动检测文件类型
fileType := DetectFileType(file.Filename)
// 返回文件访问URL相对路径
url := fmt.Sprintf("/uploads/%s/%s", dateDir, filename)
utils.Success(c, UploadResponse{
URL: url,
Name: file.Filename,
Size: file.Size,
MimeType: mimeType,
FileType: fileType,
})
}