Files
excel-api/handlers/upload.go
2026-06-26 14:31:24 +08:00

131 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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,
})
}