131 lines
3.5 KiB
Go
131 lines
3.5 KiB
Go
// 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,
|
||
})
|
||
}
|