mirror of
https://github.com/bufanyun/hotgo.git
synced 2025-08-27 23:57:09 +08:00
发布v2.13.1版本,更新内容请查看:https://github.com/bufanyun/hotgo/blob/v2.0/docs/guide-zh-CN/start-update-log.md
This commit is contained in:
@@ -14,18 +14,26 @@ import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/library/cache"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/utility/convert"
|
||||
"hotgo/utility/format"
|
||||
"hotgo/utility/url"
|
||||
"hotgo/utility/validate"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UploadDrive 存储驱动
|
||||
type UploadDrive interface {
|
||||
// Upload 上传
|
||||
Upload(ctx context.Context, file *ghttp.UploadFile) (fullPath string, err error)
|
||||
// CreateMultipart 创建分片事件
|
||||
CreateMultipart(ctx context.Context, in *CheckMultipartParams) (res *MultipartProgress, err error)
|
||||
// UploadPart 上传分片
|
||||
UploadPart(ctx context.Context, in *UploadPartParams) (res *UploadPartModel, err error)
|
||||
}
|
||||
|
||||
// New 初始化存储驱动
|
||||
@@ -70,6 +78,31 @@ func DoUpload(ctx context.Context, typ string, file *ghttp.UploadFile) (result *
|
||||
return
|
||||
}
|
||||
|
||||
if err = ValidateFileMeta(typ, meta); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result, err = HasFile(ctx, meta.Md5)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 相同存储相同身份才复用
|
||||
if result != nil && result.Drive == config.Drive && result.MemberId == contexts.GetUserId(ctx) && result.AppId == contexts.GetModule(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
// 上传到驱动
|
||||
fullPath, err := New(config.Drive).Upload(ctx, file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 写入附件记录
|
||||
return write(ctx, meta, fullPath)
|
||||
}
|
||||
|
||||
// ValidateFileMeta 验证文件元数据
|
||||
func ValidateFileMeta(typ string, meta *FileMeta) (err error) {
|
||||
if _, err = GetFileMimeType(meta.Ext); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -123,24 +156,7 @@ func DoUpload(ctx context.Context, typ string, file *ghttp.UploadFile) (result *
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
result, err = hasFile(ctx, meta.Md5)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 相同存储相同身份才复用
|
||||
if result != nil && result.Drive == config.Drive && result.MemberId == contexts.GetUserId(ctx) && result.AppId == contexts.GetModule(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
// 上传到驱动
|
||||
fullPath, err := New(config.Drive).Upload(ctx, file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 写入附件记录
|
||||
return write(ctx, meta, fullPath)
|
||||
return
|
||||
}
|
||||
|
||||
// LastUrl 根据驱动获取最终文件访问地址
|
||||
@@ -225,8 +241,8 @@ func write(ctx context.Context, meta *FileMeta, fullPath string) (models *entity
|
||||
return
|
||||
}
|
||||
|
||||
// hasFile 检查附件是否存在
|
||||
func hasFile(ctx context.Context, md5 string) (res *entity.SysAttachment, err error) {
|
||||
// HasFile 检查附件是否存在
|
||||
func HasFile(ctx context.Context, md5 string) (res *entity.SysAttachment, err error) {
|
||||
if err = GetModel(ctx).Where("md5", md5).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "检查文件hash时出现错误")
|
||||
return
|
||||
@@ -238,10 +254,147 @@ func hasFile(ctx context.Context, md5 string) (res *entity.SysAttachment, err er
|
||||
|
||||
// 只有在上传时才会检查md5值,如果附件存在则更新最后上传时间,保证上传列表更新显示在最前面
|
||||
if res.Id > 0 {
|
||||
_, _ = GetModel(ctx).WherePri(res.Id).Data(g.Map{
|
||||
update := g.Map{
|
||||
"status": consts.StatusEnabled,
|
||||
"updated_at": gtime.Now(),
|
||||
}).Update()
|
||||
}
|
||||
_, _ = GetModel(ctx).WherePri(res.Id).Data(update).Update()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CheckMultipart 检查文件分片
|
||||
func CheckMultipart(ctx context.Context, in *CheckMultipartParams) (res *CheckMultipartModel, err error) {
|
||||
res = new(CheckMultipartModel)
|
||||
|
||||
meta := new(FileMeta)
|
||||
meta.Filename = in.FileName
|
||||
meta.Size = in.Size
|
||||
meta.Ext = Ext(in.FileName)
|
||||
meta.Kind = GetFileKind(meta.Ext)
|
||||
meta.MimeType, err = GetFileMimeType(meta.Ext)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 兼容naiveUI
|
||||
naiveType := "text/plain"
|
||||
if IsImgType(Ext(in.FileName)) {
|
||||
naiveType = ""
|
||||
}
|
||||
meta.NaiveType = naiveType
|
||||
meta.Md5 = in.Md5
|
||||
|
||||
if err = ValidateFileMeta(in.UploadType, meta); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result, err := HasFile(ctx, in.Md5)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 文件已存在,直接返回。相同存储相同身份才复用
|
||||
if result != nil && result.Drive == config.Drive && result.MemberId == contexts.GetUserId(ctx) && result.AppId == contexts.GetModule(ctx) {
|
||||
res.Attachment = result
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < in.ShardCount; i++ {
|
||||
res.WaitUploadIndex = append(res.WaitUploadIndex, i+1)
|
||||
}
|
||||
|
||||
in.meta = meta
|
||||
progress, err := GetOrCreateMultipartProgress(ctx, in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(progress.UploadedIndex) > 0 {
|
||||
res.WaitUploadIndex = convert.DifferenceSlice(progress.UploadedIndex, res.WaitUploadIndex)
|
||||
}
|
||||
|
||||
if len(res.WaitUploadIndex) == 0 {
|
||||
res.WaitUploadIndex = make([]int, 0)
|
||||
}
|
||||
res.UploadId = progress.UploadId
|
||||
res.Progress = CalcUploadProgress(progress.UploadedIndex, progress.ShardCount)
|
||||
res.SizeFormat = format.FileSize(progress.Meta.Size)
|
||||
return
|
||||
}
|
||||
|
||||
// CalcUploadProgress 计算上传进度
|
||||
func CalcUploadProgress(uploadedIndex []int, shardCount int) float64 {
|
||||
return format.Round2Float64(float64(len(uploadedIndex)) / float64(shardCount) * 100)
|
||||
}
|
||||
|
||||
// GenUploadId 生成上传ID
|
||||
func GenUploadId(ctx context.Context, md5 string) string {
|
||||
return fmt.Sprintf("%v:%v:%v@%v", md5, contexts.GetUserId(ctx), contexts.GetModule(ctx), config.Drive)
|
||||
}
|
||||
|
||||
// GetOrCreateMultipartProgress 获取或创建分片上传事件进度
|
||||
func GetOrCreateMultipartProgress(ctx context.Context, in *CheckMultipartParams) (res *MultipartProgress, err error) {
|
||||
uploadId := GenUploadId(ctx, in.Md5)
|
||||
res, err = GetMultipartProgress(ctx, uploadId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res != nil {
|
||||
return res, nil
|
||||
}
|
||||
return New(config.Drive).CreateMultipart(ctx, in)
|
||||
}
|
||||
|
||||
// GetMultipartProgress 获取分片上传事件进度
|
||||
func GetMultipartProgress(ctx context.Context, uploadId string) (res *MultipartProgress, err error) {
|
||||
key := fmt.Sprintf("%v:%v", consts.CacheMultipartUpload, uploadId)
|
||||
get, err := cache.Instance().Get(ctx, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = get.Scan(&res)
|
||||
return
|
||||
}
|
||||
|
||||
// CreateMultipartProgress 创建分片上传事件进度
|
||||
func CreateMultipartProgress(ctx context.Context, in *MultipartProgress) (err error) {
|
||||
key := fmt.Sprintf("%v:%v", consts.CacheMultipartUpload, in.UploadId)
|
||||
return cache.Instance().Set(ctx, key, in, time.Hour*24*7)
|
||||
}
|
||||
|
||||
// UpdateMultipartProgress 更新分片上传事件进度
|
||||
func UpdateMultipartProgress(ctx context.Context, in *MultipartProgress) (err error) {
|
||||
key := fmt.Sprintf("%v:%v", consts.CacheMultipartUpload, in.UploadId)
|
||||
return cache.Instance().Set(ctx, key, in, time.Hour*24*7)
|
||||
}
|
||||
|
||||
// DelMultipartProgress 删除分片上传事件进度
|
||||
func DelMultipartProgress(ctx context.Context, in *MultipartProgress) (err error) {
|
||||
key := fmt.Sprintf("%v:%v", consts.CacheMultipartUpload, in.UploadId)
|
||||
_, err = cache.Instance().Remove(ctx, key)
|
||||
return
|
||||
}
|
||||
|
||||
// UploadPart 上传分片
|
||||
func UploadPart(ctx context.Context, in *UploadPartParams) (res *UploadPartModel, err error) {
|
||||
in.mp, err = GetMultipartProgress(ctx, in.UploadId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if in.mp == nil {
|
||||
err = gerror.New("分片事件不存在,请重新上传!")
|
||||
return
|
||||
}
|
||||
|
||||
if validate.InSlice(in.mp.UploadedIndex, in.Index) {
|
||||
err = gerror.New("该分片已上传过了")
|
||||
return
|
||||
}
|
||||
|
||||
res, err = New(config.Drive).UploadPart(ctx, in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
Reference in New Issue
Block a user