Files
Go-Web-Template/server/utils/upload/local.go
2026-04-28 20:00:05 +08:00

118 lines
3.4 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 upload
import (
"errors"
"io"
"mime/multipart"
"os"
"path/filepath"
"strings"
"sync"
"time"
"git.echol.cn/loser/ai_wanjia/global"
"git.echol.cn/loser/ai_wanjia/utils"
"go.uber.org/zap"
)
var mu sync.Mutex
type Local struct{}
//@author: [piexlmax](https://github.com/piexlmax)
//@author: [ccfish86](https://github.com/ccfish86)
//@author: [SliverHorn](https://github.com/SliverHorn)
//@object: *Local
//@function: UploadFile
//@description: 上传文件按来源平台和用户ID分目录存储
//@param: file *multipart.FileHeader, ctx UploadContext
//@return: string, string, error
func (*Local) UploadFile(file *multipart.FileHeader, ctx UploadContext) (string, string, error) {
// 读取文件后缀
ext := filepath.Ext(file.Filename)
// 读取文件名并加密
name := strings.TrimSuffix(file.Filename, ext)
name = utils.MD5V([]byte(name))
// 拼接新文件名
filename := name + "_" + time.Now().Format("20060102150405") + ext
// 生成按平台+用户+日期分组的子目录
subDir := ctx.SubDir()
storePath := filepath.Join(global.GVA_CONFIG.Local.StorePath, subDir)
// 尝试创建目录
mkdirErr := os.MkdirAll(storePath, os.ModePerm)
if mkdirErr != nil {
global.GVA_LOG.Error("function os.MkdirAll() failed", zap.Any("err", mkdirErr.Error()))
return "", "", errors.New("function os.MkdirAll() failed, err:" + mkdirErr.Error())
}
// 拼接绝对存储路径和访问路径
p := filepath.Join(storePath, filename)
// key 为相对路径(含子目录),用于后续删除时定位文件
key := subDir + "/" + filename
// 若配置了域名前缀,则返回完整 URL否则返回相对路径
fileURL := global.GVA_CONFIG.Local.Path + "/" + key
if base := strings.TrimRight(global.GVA_CONFIG.Local.BaseURL, "/"); base != "" {
fileURL = base + "/" + global.GVA_CONFIG.Local.Path + "/" + key
}
f, openError := file.Open()
if openError != nil {
global.GVA_LOG.Error("function file.Open() failed", zap.Any("err", openError.Error()))
return "", "", errors.New("function file.Open() failed, err:" + openError.Error())
}
defer f.Close()
out, createErr := os.Create(p)
if createErr != nil {
global.GVA_LOG.Error("function os.Create() failed", zap.Any("err", createErr.Error()))
return "", "", errors.New("function os.Create() failed, err:" + createErr.Error())
}
defer out.Close()
_, copyErr := io.Copy(out, f)
if copyErr != nil {
global.GVA_LOG.Error("function io.Copy() failed", zap.Any("err", copyErr.Error()))
return "", "", errors.New("function io.Copy() failed, err:" + copyErr.Error())
}
return fileURL, key, nil
}
//@author: [piexlmax](https://github.com/piexlmax)
//@author: [ccfish86](https://github.com/ccfish86)
//@author: [SliverHorn](https://github.com/SliverHorn)
//@object: *Local
//@function: DeleteFile
//@description: 删除文件
//@param: key string
//@return: error
func (*Local) DeleteFile(key string) error {
if key == "" {
return errors.New("key不能为空")
}
// 防止路径遍历:拒绝 .. 及非法字符(/ 允许,用于子目录)
if strings.Contains(key, "..") || strings.ContainsAny(key, `\*?"<>|`) {
return errors.New("非法的key")
}
p := filepath.Join(global.GVA_CONFIG.Local.StorePath, key)
if _, err := os.Stat(p); os.IsNotExist(err) {
return errors.New("文件不存在")
}
mu.Lock()
defer mu.Unlock()
err := os.Remove(p)
if err != nil {
return errors.New("文件删除失败: " + err.Error())
}
return nil
}