Skip to content

Commit c38053f

Browse files
committed
perf: 使用并发优化上传效率
1 parent 7c9804b commit c38053f

File tree

4 files changed

+83
-64
lines changed

4 files changed

+83
-64
lines changed

.golangci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ linters-settings:
2121
# 默认失败置信度
2222
confidence: 0.1
2323
rules:
24+
- name: unchecked-type-assertion
25+
disabled: true
2426
- name: var-naming # 变量命名规则
2527
severity: warning
2628
disabled: true

internal/controllers/objectController/upload.go

Lines changed: 64 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"mime/multipart"
88
"path/filepath"
9+
"sync"
910

1011
"github.com/gin-gonic/gin"
1112
uuid "github.com/satori/go.uuid"
@@ -44,71 +45,79 @@ func BatchUploadFiles(c *gin.Context) {
4445
return
4546
}
4647

47-
results := make([]uploadFileRespElement, 0)
48-
for _, fileHeader := range data.Files {
49-
element := uploadFileRespElement{
50-
Filename: fileHeader.Filename,
51-
}
48+
var mutex sync.Mutex
49+
var wg sync.WaitGroup
50+
results := make([]uploadFileRespElement, 0, len(data.Files))
51+
for _, f := range data.Files {
52+
wg.Add(1)
53+
go func(fileHeader *multipart.FileHeader) {
54+
defer wg.Done()
55+
56+
res := handleSingleUpload(fileHeader, &data, bucket, c.ClientIP())
57+
mutex.Lock()
58+
results = append(results, res)
59+
mutex.Unlock()
60+
}(f)
61+
}
5262

53-
fileSize := fileHeader.Size
54-
if fileSize > objectService.SizeLimit {
55-
element.Error = apiException.FileSizeExceedError.Error()
56-
results = append(results, element)
57-
continue
58-
}
63+
wg.Wait()
64+
response.JsonSuccessResp(c, gin.H{
65+
"results": results,
66+
})
67+
}
5968

60-
filename := fileHeader.Filename
61-
ext := filepath.Ext(filename) // 获取文件扩展名
62-
name := filename[:len(filename)-len(ext)] // 获取去掉扩展名的文件名
69+
func handleSingleUpload(
70+
fileHeader *multipart.FileHeader, data *batchUploadFileData, bucket oss.StorageProvider, ip string,
71+
) uploadFileRespElement {
72+
element := uploadFileRespElement{
73+
Filename: fileHeader.Filename,
74+
}
6375

64-
// 若使用 UUID 作为文件名
65-
if data.UseUUID {
66-
name = uuid.NewV1().String()
67-
}
76+
if fileHeader.Size > objectService.SizeLimit {
77+
element.Error = apiException.FileSizeExceedError.Error()
78+
return element
79+
}
6880

69-
file, err := fileHeader.Open()
70-
if err != nil {
71-
element.Error = apiException.UploadFileError.Error()
72-
results = append(results, element)
73-
continue
74-
}
81+
filename := fileHeader.Filename
82+
ext := filepath.Ext(filename) // 获取文件扩展名
83+
name := filename[:len(filename)-len(ext)] // 获取去掉扩展名的文件名
7584

76-
// 转换到 WebP
77-
var reader io.ReadSeeker = file
78-
if data.ConvertWebP {
79-
reader, err = objectService.ConvertToWebP(file)
80-
ext = ".webp"
81-
if errors.Is(err, image.ErrFormat) {
82-
element.Error = apiException.FileNotImageError.Error()
83-
results = append(results, element)
84-
continue
85-
}
86-
if err != nil {
87-
element.Error = apiException.ServerError.Error()
88-
results = append(results, element)
89-
continue
90-
}
91-
}
85+
// 若使用 UUID 作为文件名
86+
if data.UseUUID {
87+
name = uuid.NewV1().String()
88+
}
9289

93-
// 上传文件
94-
objectKey := objectService.GenerateObjectKey(data.Location, name, ext)
95-
err = bucket.SaveObject(reader, objectKey)
90+
file, err := fileHeader.Open()
91+
if err != nil {
92+
element.Error = apiException.UploadFileError.Error()
93+
return element
94+
}
95+
defer func() { _ = file.Close() }()
96+
97+
// 转换到 WebP
98+
var reader io.ReadSeeker = file
99+
if data.ConvertWebP {
100+
reader, err = objectService.ConvertToWebP(file)
101+
ext = ".webp"
102+
if errors.Is(err, image.ErrFormat) {
103+
element.Error = apiException.FileNotImageError.Error()
104+
return element
105+
}
96106
if err != nil {
97107
element.Error = apiException.ServerError.Error()
98-
results = append(results, element)
99-
continue
108+
return element
100109
}
110+
}
101111

102-
element.ObjectKey = objectKey
103-
results = append(results, element)
104-
105-
zap.L().Info("上传文件成功", zap.String("bucket", data.Bucket), zap.String("objectKey", objectKey), zap.String("ip", c.ClientIP()))
106-
107-
// 关闭文件
108-
_ = file.Close()
112+
// 上传文件
113+
objectKey := objectService.GenerateObjectKey(data.Location, name, ext)
114+
err = bucket.SaveObject(reader, objectKey)
115+
if err != nil {
116+
element.Error = apiException.ServerError.Error()
117+
return element
109118
}
110119

111-
response.JsonSuccessResp(c, gin.H{
112-
"results": results,
113-
})
120+
element.ObjectKey = objectKey
121+
zap.L().Info("上传文件成功", zap.String("bucket", data.Bucket), zap.String("objectKey", objectKey), zap.String("ip", ip))
122+
return element
114123
}

internal/services/objectService/service.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import (
88
_ "image/png"
99
"io"
1010
"path"
11+
"regexp"
1112
"strings"
13+
"sync"
1214

1315
"github.com/chai2010/webp"
1416
"github.com/dustin/go-humanize"
@@ -21,6 +23,14 @@ import (
2123
// SizeLimit 上传大小限制
2224
var SizeLimit = humanize.MiByte * config.Config.GetInt64("oss.limit")
2325

26+
var invalidCharRegex = regexp.MustCompile(`[:*?"<>|]`)
27+
28+
var bufferPool = sync.Pool{
29+
New: func() any {
30+
return new(bytes.Buffer)
31+
},
32+
}
33+
2434
// GenerateObjectKey 通过路径和文件名生成 ObjectKey
2535
func GenerateObjectKey(location string, filename string, fileExt string) string {
2636
return path.Join(CleanLocation(location), filename+fileExt)
@@ -29,12 +39,7 @@ func GenerateObjectKey(location string, filename string, fileExt string) string
2939
// CleanLocation 清理以避免非法路径
3040
func CleanLocation(location string) string {
3141
isDir := strings.HasSuffix(location, "/")
32-
loc := location
33-
invalidChars := []string{":", "*", "?", "<", ">", "|", "\""}
34-
for _, char := range invalidChars {
35-
loc = strings.ReplaceAll(loc, char, "")
36-
}
37-
42+
loc := invalidCharRegex.ReplaceAllString(location, "")
3843
result := strings.TrimLeft(path.Clean(loc), "./\\")
3944
if isDir {
4045
result += "/"
@@ -49,8 +54,11 @@ func ConvertToWebP(reader io.Reader) (*bytes.Reader, error) {
4954
return nil, err
5055
}
5156

52-
var buf bytes.Buffer
53-
err = webp.Encode(&buf, img, &webp.Options{Quality: 100})
57+
buf, _ := bufferPool.Get().(*bytes.Buffer)
58+
buf.Reset()
59+
defer bufferPool.Put(buf)
60+
61+
err = webp.Encode(buf, img, &webp.Options{Quality: 100})
5462
if err != nil {
5563
return nil, err
5664
}

pkg/oss/local.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func (p *LocalStorageProvider) GetFileList(prefix string) ([]FileListElement, er
119119
return nil, err
120120
}
121121

122-
list := make([]FileListElement, 0)
122+
list := make([]FileListElement, 0, len(fileList))
123123
for _, file := range fileList {
124124
fileInfo, err := file.Info()
125125
if err != nil {

0 commit comments

Comments
 (0)