Files
tamabox/internal/storage/oss.go
T
tamakyi 08bbd17751 storage: 对齐图片对象 key 到生产口径
生产真实 key 为 `YYYY/MM/<UnixNano>.<ext>`(无前缀、无 day 层),
与 route/question/page.go:uploadImage 一致;internal/storage 里三个
上传函数仍用旧的 `picture/ 前缀 + YYYY/MM/DD + 15hex + 无扩展名`,从未上线。

- PictureKeyPrefix 置空,统一由 uploadKey 生成 key
- 扩展名跟随最终 ContentType(webp 转成功用 .webp,回退原图用原格式)
- OSS 原图直传通道取原文件名扩展名
- 新增 picture_test.go 锁定 key 格式,防回归
2026-10-29 20:04:31 +08:00

65 lines
1.8 KiB
Go

// Copyright 2022 E99p1ant. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package storage
import (
"fmt"
"io"
"mime/multipart"
"github.com/pkg/errors"
"github.com/wuhan005/gadget"
"github.com/tamakyi/TamaBox/internal/conf"
)
// UploadPictureToOSS upload user's avatar or background image to OSS.
// It returns the uploaded asset URL.
func UploadPictureToOSS(file multipart.File, fileHeader *multipart.FileHeader) (string, error) {
// 读取文件头,校验是否为图片
header := make([]byte, 512)
n, err := file.Read(header)
if err != nil && err != io.EOF {
return "", errors.Wrap(err, "read file header")
}
if n == 0 {
return "", errors.New("empty file")
}
if !isSupportedImage(header[:n]) {
return "", errors.New("不支持的文件格式,仅允许 JPEG、PNG、GIF、WebP 图片")
}
// 重置文件指针
if _, err := file.Seek(0, io.SeekStart); err != nil {
return "", errors.Wrap(err, "seek file")
}
// 获取共享 OSS bucket
bucket, err := GetOSSBucket()
if err != nil {
return "", errors.Wrap(err, "get OSS bucket")
}
// OSS 通道原样上传原图(不转 WebP),扩展名沿用原始文件名。
ext := ""
if fileHeader != nil {
ext = originalExt(fileHeader.Filename)
}
key := uploadKey(ext)
if err := gadget.Retry(5, func() error {
if err := bucket.PutObject(key, file); err != nil {
return errors.Wrap(err, "put object")
}
return nil
}); err != nil {
return "", errors.Wrap(err, "retry 5 times")
}
if conf.Upload.AliyunBucketCDNHost != "" {
return fmt.Sprintf("https://%s/%s", conf.Upload.AliyunBucketCDNHost, key), nil
}
return fmt.Sprintf("https://%s.%s/%s", conf.Upload.AliyunBucket, conf.Upload.AliyunEndpoint, key), nil
}