310 lines
8.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.

/*
* @desc:上传处理
* @company:云南奇讯科技有限公司
* @Author: yixiaohu<yxh669@qq.com>
* @Date: 2022/9/28 9:37
*/
package upload
import (
"context"
"crypto/md5"
"encoding/hex"
"errors"
"fmt"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/tiger1103/gfast/v3/internal/app/common/consts"
"github.com/tiger1103/gfast/v3/internal/app/common/model"
"github.com/tiger1103/gfast/v3/internal/app/common/model/entity"
"github.com/tiger1103/gfast/v3/internal/app/common/service"
"github.com/tiger1103/gfast/v3/library/libUtils"
"github.com/tiger1103/gfast/v3/library/liberr"
"github.com/tiger1103/gfast/v3/library/upload"
"io"
)
func init() {
service.RegisterUpload(New())
}
func New() service.IUpload {
return &sUpload{}
}
type sUpload struct{}
// UploadFiles 上传多文件
func (s *sUpload) UploadFiles(ctx context.Context, files []*ghttp.UploadFile, checkFileType string, source int, userId uint64, appId string) (result []*model.UploadResponse, err error) {
for _, item := range files {
f, e := s.UploadFile(ctx, item, checkFileType, source, userId, appId)
if e != nil {
return
}
result = append(result, f)
}
return
}
// UploadFile 上传单文件
func (s *sUpload) UploadFile(ctx context.Context,
file *ghttp.UploadFile, checkFileType string, source int,
userId uint64, appId string) (result *model.UploadResponse, err error) {
err = g.Try(ctx, func(ctx context.Context) {
// 检查文件类型
err = s.CheckType(ctx, checkFileType, file.Filename)
liberr.ErrIsNil(ctx, err)
// 检查文件大小
err = s.CheckSize(ctx, checkFileType, file.Size)
liberr.ErrIsNil(ctx, err)
//判断该文件是否已经上传过,上传过则不再上传
var (
md5 = ""
existsFile *model.SysAttachmentInfoRes
)
md5, err = s.computeMD5(file)
liberr.ErrIsNil(ctx, err)
existsFile, err = service.SysAttachment().GetByMd5(ctx, md5)
liberr.ErrIsNil(ctx, err, "获取文件信息失败")
if existsFile != nil {
result = &model.UploadResponse{
Size: existsFile.Size,
Path: existsFile.Path,
FullPath: libUtils.GetDomain(ctx, true) + "/" + existsFile.Path,
Name: existsFile.Name,
Type: existsFile.MimeType,
}
return
}
uploader := upload.GetUploader(upload.UploaderType(source))
if uploader == nil {
liberr.ErrIsNil(ctx, errors.New("没有找到上传适配器"))
}
result, err = uploader.Upload(ctx, file)
liberr.ErrIsNil(ctx, err)
//保存上传文件到数据库
err = service.SysAttachment().AddUpload(ctx, result, &model.SysAttachmentAddAttribute{
Md5: md5,
Driver: gconv.Uint(source),
UserId: userId,
AppId: appId,
})
liberr.ErrIsNil(ctx, err)
})
return
}
// CheckSize 检查上传文件大小
func (s *sUpload) CheckSize(ctx context.Context, checkFileType string, fileSize int64) (err error) {
var (
configSize *entity.SysConfig
)
if checkFileType == consts.CheckFileTypeFile {
//获取上传大小配置
configSize, err = s.getUpConfig(ctx, consts.FileSizeKey)
if err != nil {
return
}
} else if checkFileType == consts.CheckFileTypeImg {
//获取上传大小配置
configSize, err = s.getUpConfig(ctx, consts.ImgSizeKey)
if err != nil {
return
}
} else {
return errors.New(fmt.Sprintf("文件检查类型错误:%s|%s", consts.CheckFileTypeFile, consts.CheckFileTypeImg))
}
var rightSize bool
rightSize, err = s.checkSize(configSize.ConfigValue, fileSize)
if err != nil {
return
}
if !rightSize {
err = gerror.New("上传文件超过最大尺寸:" + configSize.ConfigValue)
return
}
return
}
// CheckType 检查上传文件类型
func (s *sUpload) CheckType(ctx context.Context, checkFileType string, fileName string) (err error) {
var (
configType *entity.SysConfig
)
if checkFileType == consts.CheckFileTypeFile {
//获取上传类型配置
configType, err = s.getUpConfig(ctx, consts.FileTypeKey)
if err != nil {
return
}
} else if checkFileType == consts.CheckFileTypeImg {
//获取上传类型配置
configType, err = s.getUpConfig(ctx, consts.ImgTypeKey)
if err != nil {
return
}
} else {
return errors.New(fmt.Sprintf("文件检查类型错误:%s|%s", consts.CheckFileTypeFile, consts.CheckFileTypeImg))
}
rightType := s.checkFileType(fileName, configType.ConfigValue)
if !rightType {
err = gerror.New("上传文件类型错误,只能包含后缀为:" + configType.ConfigValue + "的文件。")
return
}
return
}
// 获取上传配置
func (s *sUpload) getUpConfig(ctx context.Context, key string) (config *entity.SysConfig, err error) {
config, err = service.SysConfig().GetConfigByKey(ctx, key)
if err != nil {
return
}
if config == nil {
err = gerror.New("上传文件类型未设置,请在后台配置")
return
}
return
}
// 判断上传文件类型是否合法
func (s *sUpload) checkFileType(fileName, typeString string) bool {
suffix := gstr.SubStrRune(fileName, gstr.PosRRune(fileName, ".")+1, gstr.LenRune(fileName)-1)
imageType := gstr.Split(typeString, ",")
rightType := false
for _, v := range imageType {
if gstr.Equal(suffix, v) {
rightType = true
break
}
}
return rightType
}
// 检查文件大小是否合法
func (s *sUpload) checkSize(configSize string, fileSize int64) (bool, error) {
match, err := gregex.MatchString(`^([0-9]+)(?i:([a-z]*))$`, configSize)
if err != nil {
return false, err
}
if len(match) == 0 {
err = gerror.New("上传文件大小未设置请在后台配置格式为30M,30k,30MB")
return false, err
}
var cfSize int64
switch gstr.ToUpper(match[2]) {
case "MB", "M":
cfSize = gconv.Int64(match[1]) * 1024 * 1024
case "KB", "K":
cfSize = gconv.Int64(match[1]) * 1024
case "":
cfSize = gconv.Int64(match[1])
}
if cfSize == 0 {
err = gerror.New("上传文件大小未设置请在后台配置格式为30M,30k,30MB最大单位为MB")
return false, err
}
return cfSize >= fileSize, nil
}
// 静态文件夹目录
func (s *sUpload) getStaticPath(ctx context.Context) string {
value, _ := g.Cfg().Get(ctx, "server.serverRoot")
if !value.IsEmpty() {
return value.String()
}
return ""
}
// computeMD5 计算给定文件的 MD5 值
func (s *sUpload) computeMD5(upFile *ghttp.UploadFile) (string, error) {
file, err := upFile.Open()
if err != nil {
return "", err
}
defer file.Close()
// 创建一个新的 MD5 哈希对象
hash := md5.New()
// 逐块读取文件内容并更新哈希
if _, err := io.Copy(hash, file); err != nil {
return "", err
}
// 将 MD5 值转换为十六进制字符串
return hex.EncodeToString(hash.Sum(nil)), nil
}
func (s *sUpload) CheckMultipart(ctx context.Context, req *model.CheckMultipartReq) (res *model.CheckMultipartRes, err error) {
err = g.Try(ctx, func(ctx context.Context) {
// 检查文件类型
err = s.CheckType(ctx, req.UploadType, req.FileName)
liberr.ErrIsNil(ctx, err)
// 检查文件大小
err = s.CheckSize(ctx, req.UploadType, req.Size)
liberr.ErrIsNil(ctx, err)
//检查文件是否已经上传
var existsFile *model.SysAttachmentInfoRes
existsFile, err = service.SysAttachment().GetByMd5(ctx, req.Md5)
liberr.ErrIsNil(ctx, err, "获取文件信息失败")
res = new(model.CheckMultipartRes)
if existsFile != nil {
res.Attachment = &model.UploadResponse{
Size: existsFile.Size,
Path: existsFile.Path,
FullPath: libUtils.GetDomain(ctx, true) + "/" + existsFile.Path,
Name: existsFile.Name,
Type: existsFile.MimeType,
}
return
}
uploader := upload.GetUploader(upload.UploaderType(req.DriverType))
if uploader == nil {
liberr.ErrIsNil(ctx, errors.New("没有找到上传适配器"))
}
res, err = uploader.CheckMultipart(ctx, req)
liberr.ErrIsNil(ctx, err)
})
return
}
func (s *sUpload) UploadPart(ctx context.Context, req *model.UploadPartReq) (res *model.UploadPartRes, err error) {
err = g.Try(ctx, func(ctx context.Context) {
uploader := upload.GetUploader(upload.UploaderType(req.DriverType))
if uploader == nil {
liberr.ErrIsNil(ctx, errors.New("没有找到上传适配器"))
}
res, err = uploader.UploadPart(ctx, req)
liberr.ErrIsNil(ctx, err)
//保存上传文件到数据库
if res != nil && res.Finish {
err = service.SysAttachment().AddUpload(ctx, res.Attachment, &model.SysAttachmentAddAttribute{
Md5: req.Md5,
Driver: gconv.Uint(req.DriverType),
UserId: req.UserId,
AppId: req.AppId,
})
liberr.ErrIsNil(ctx, err)
}
})
return
}