测试修复文件上传 bug

This commit is contained in:
yml2213
2026-06-12 19:48:38 +08:00
parent a615100730
commit 4344b1d07c
4 changed files with 69 additions and 2 deletions
+1
View File
@@ -87,6 +87,7 @@ func (h *Handler) writeObject(c *gin.Context, publicOnly bool) {
}
func writeFileError(c *gin.Context, err error) {
_ = c.Error(err)
switch {
case errors.Is(err, ErrDependencyUnavailable):
response.ServiceUnavailable(c, "文件存储未连接")
+2 -1
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"errors"
"fmt"
"io"
"mime/multipart"
"net/http"
@@ -56,7 +57,7 @@ func (s *Service) Upload(req uploadRequest) (*UploadDTO, error) {
if err := s.storage.PutObject(req.Context, key, bytes.NewReader(data), int64(len(data)), contentType, map[string]string{
"original-filename": req.Header.Filename,
}); err != nil {
return nil, err
return nil, fmt.Errorf("%w: %v", ErrDependencyUnavailable, err)
}
var thumbnailURL string
var mediumURL string
+53 -1
View File
@@ -70,8 +70,18 @@ func (s *Storage) PutObject(ctx context.Context, key string, reader io.Reader, s
}
_, err := s.client.PutObject(ctx, s.bucket, key, reader, size, minio.PutObjectOptions{
ContentType: contentType,
UserMetadata: metadata,
UserMetadata: sanitizeObjectMetadata(metadata),
})
if isNoSuchBucketError(err) && rewindReader(reader) == nil {
s.markBucketNotReady()
if readyErr := s.ensureBucketReady(ctx); readyErr != nil {
return readyErr
}
_, err = s.client.PutObject(ctx, s.bucket, key, reader, size, minio.PutObjectOptions{
ContentType: contentType,
UserMetadata: sanitizeObjectMetadata(metadata),
})
}
return err
}
@@ -120,6 +130,48 @@ func (s *Storage) ensureBucketReady(ctx context.Context) error {
return s.ensureBucket(ctx)
}
func (s *Storage) markBucketNotReady() {
s.bucketMu.Lock()
defer s.bucketMu.Unlock()
s.bucketReady = false
}
func isNoSuchBucketError(err error) bool {
if err == nil {
return false
}
errResp := minio.ToErrorResponse(err)
if errResp.Code == "NoSuchBucket" {
return true
}
return strings.Contains(strings.ToLower(err.Error()), "bucket does not exist")
}
func rewindReader(reader io.Reader) error {
seeker, ok := reader.(io.Seeker)
if !ok {
return fmt.Errorf("reader cannot rewind")
}
_, err := seeker.Seek(0, io.SeekStart)
return err
}
func sanitizeObjectMetadata(metadata map[string]string) map[string]string {
if len(metadata) == 0 {
return nil
}
sanitized := make(map[string]string, len(metadata))
for key, value := range metadata {
key = strings.TrimSpace(key)
if key == "" {
continue
}
// MinIO/S3 用户元数据会作为 HTTP header 发送,值统一转义为 ASCII,避免中文文件名导致上传失败。
sanitized[key] = url.QueryEscape(value)
}
return sanitized
}
func normalizeEndpoint(raw string) (string, bool, error) {
parsed, err := url.Parse(raw)
if err != nil {
@@ -0,0 +1,13 @@
package file
import "testing"
func TestSanitizeObjectMetadataEscapesNonASCII(t *testing.T) {
got := sanitizeObjectMetadata(map[string]string{
"original-filename": "游戏ID截图 1.png",
})
if got["original-filename"] != "%E6%B8%B8%E6%88%8FID%E6%88%AA%E5%9B%BE+1.png" {
t.Fatalf("metadata filename = %q", got["original-filename"])
}
}