修改文件上传逻辑

This commit is contained in:
2025-10-31 17:36:04 +08:00
parent 49f948b062
commit e882c335e4

View File

@@ -1,13 +1,20 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
// fileupload/upload_file_logic.go
package fileupload
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"path/filepath"
"sort"
"strings"
"time"
"github.com/JACKYMYPERSON/hldrCenter/config"
"github.com/JACKYMYPERSON/hldrCenter/internal/file/internal/types"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
"github.com/gin-gonic/gin"
"github.com/zeromicro/go-zero/core/logx"
@@ -30,7 +37,203 @@ func NewUploadFileLogic(ctx context.Context, cfg *config.Config, c *gin.Context)
}
func (l *UploadFileLogic) UploadFile() (resp *types.UploadFileResp, err error) {
// todo: add your logic here and delete this line
// 1. 获取上传文件
fileHeader, err := l.c.FormFile("file")
if err != nil {
l.c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "获取文件失败,请重新上传",
})
return
}
// 2. 打开文件
file, err := fileHeader.Open()
if err != nil {
l.c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "打开文件失败",
})
return
}
defer file.Close()
// 3. 检查文件大小
if fileHeader.Size > l.cfg.FileUpload.MaxFileSize {
l.c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": fmt.Sprintf("文件大小不能超过 %dMB", l.cfg.FileUpload.MaxFileSize>>20),
})
return
}
// 4. 检查文件类型
fileType := fileHeader.Header.Get("Content-Type")
allowTypes := strings.Split(l.cfg.FileUpload.AllowFileTypes, ",")
isAllowed := false
for _, typ := range allowTypes {
if strings.TrimSpace(typ) == fileType {
isAllowed = true
break
}
}
if !isAllowed {
l.c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": fmt.Sprintf("不支持的文件类型,仅允许:%s", l.cfg.FileUpload.AllowFileTypes),
})
return
}
// 5. 初始化OSS客户端
client, err := oss.New(
l.cfg.OSS.Endpoint,
l.cfg.OSS.AccessKeyID,
l.cfg.OSS.AccessKeySecret,
)
if err != nil {
l.c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "初始化OSS客户端失败",
})
return
}
// 6. 获取Bucket实例
bucket, err := client.Bucket(l.cfg.OSS.BucketName)
if err != nil {
l.c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "获取Bucket失败",
})
return
}
// 7. 生成文件存储路径
timestamp := time.Now().Format("20060102150405")
fileExt := filepath.Ext(fileHeader.Filename)
filename := fmt.Sprintf("%s_%s%s", timestamp, strings.TrimSuffix(fileHeader.Filename, fileExt), fileExt)
var fileDir string
switch {
case strings.HasPrefix(fileType, "video/"):
fileDir = "videos/"
case fileType == "application/pdf":
fileDir = "pdfs/"
case strings.HasPrefix(fileType, "image/"):
fileDir = "images/"
case fileType == "text/plain":
fileDir = "txts/"
case fileType == "application/json":
fileDir = "jsons/"
default:
fileDir = "other/"
}
objectKey := l.cfg.OSS.ObjectPrefix + fileDir + filename
// 8. 分片上传100KB/片)
const partSize = 100 * 1024 // 100KB
totalSize := fileHeader.Size
partCount := (totalSize + partSize - 1) / partSize
var partETags []oss.PartETag
// 重置文件指针
if _, err = file.Seek(0, 0); err != nil {
l.c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "重置文件指针失败",
})
return
}
// 8.1 初始化分片上传
imur, err := bucket.InitiateMultipartUpload(objectKey)
if err != nil {
l.c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "初始化分片上传失败",
})
return
}
// 8.2 上传每个分片
for i := 1; i <= int(partCount); i++ {
partStart := int64((i - 1) * partSize)
partEnd := partStart + partSize
if partEnd > totalSize {
partEnd = totalSize
}
partLen := partEnd - partStart
// 读取分片数据
data := make([]byte, partLen)
if _, err = file.Seek(partStart, 0); err != nil {
_ = bucket.AbortMultipartUpload(imur)
l.c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": fmt.Sprintf("定位分片%d失败", i),
})
return
}
if _, err = io.ReadFull(file, data); err != nil && err != io.EOF {
_ = bucket.AbortMultipartUpload(imur)
l.c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": fmt.Sprintf("读取分片%d失败", i),
})
return
}
// 上传分片(旧版:传入 imur
partResult, err := bucket.UploadPart(imur, i, bytes.NewReader(data))
if err != nil {
_ = bucket.AbortMultipartUpload(imur)
l.c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": fmt.Sprintf("上传分片%d失败", i),
})
return
}
partETags = append(partETags, oss.PartETag{
PartNumber: i,
ETag: partResult.ETag,
})
}
// 8.3 按分片号排序(必须)
sort.Slice(partETags, func(i, j int) bool {
return partETags[i].PartNumber < partETags[j].PartNumber
})
// 8.4 完成分片上传(旧版:传入 imur
_, err = bucket.CompleteMultipartUpload(imur, partETags)
if err != nil {
_ = bucket.AbortMultipartUpload(imur)
l.c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "合并分片失败",
})
return
}
// 9. 返回成功结果
fileURL := fmt.Sprintf("https://%s.%s/%s",
l.cfg.OSS.BucketName,
strings.TrimPrefix(l.cfg.OSS.Endpoint, "https://"),
objectKey,
)
l.c.JSON(http.StatusOK, gin.H{
"code": 200,
"message": "文件上传成功",
"data": gin.H{
"url": fileURL,
"filename": fileHeader.Filename,
"size": fileHeader.Size,
"type": fileType,
},
})
return nil, nil
}