101 lines
3.1 KiB
Go
101 lines
3.1 KiB
Go
// set-oss-cors/main.go
|
|
// 给 OSS bucket 设置 CORS 规则,允许浏览器跨域读取预签名 URL。
|
|
// 背景:
|
|
// - 前端 useLaserSegment.downloadToLocal 通过 fetch 直连 OSS 拉取 cutout/png
|
|
// - OSS bucket 未配 CORS 时,浏览器在 CORS 预检后中断响应,
|
|
// 表现为 net::ERR_CONTENT_LENGTH_MISMATCH 200 (OK) 或 TypeError: Failed to fetch
|
|
//
|
|
// 用法:
|
|
//
|
|
// go run scripts/set-oss-cors/main.go
|
|
//
|
|
// (会自动从 backend/.env / docker/.env.prod 读取 OSS_REGION / OSS_BUCKET_NAME /
|
|
// OSS_ACCESS_KEY_ID / OSS_ACCESS_KEY_SECRET)
|
|
//
|
|
// 注意:aliyun-oss-go-sdk v3.0.2 的 CORS 方法挂在 *Client 上,不是 *Bucket。
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
|
"github.com/joho/godotenv"
|
|
"github.com/topfans/backend/gateway/config"
|
|
)
|
|
|
|
func main() {
|
|
// 多路径 fallback:本地 .env / docker/.env.prod / docker/.env / .env
|
|
for _, p := range []string{
|
|
"backend/.env",
|
|
"../.env",
|
|
".env",
|
|
"docker/.env.prod",
|
|
"docker/.env",
|
|
"../docker/.env.prod",
|
|
"../docker/.env",
|
|
} {
|
|
if _, err := os.Stat(p); err == nil {
|
|
_ = godotenv.Load(p)
|
|
}
|
|
}
|
|
|
|
cfg := config.Load()
|
|
|
|
fmt.Println("=== SetBucketCORS ===")
|
|
fmt.Printf("Bucket: %s Region: %s\n", cfg.OSS.BucketName, cfg.OSS.Region)
|
|
fmt.Printf("AK: %s\n", mask(cfg.OSS.AccessKeyID))
|
|
|
|
endpoint := fmt.Sprintf("https://oss-%s.aliyuncs.com", cfg.OSS.Region)
|
|
client, err := oss.New(endpoint, cfg.OSS.AccessKeyID, cfg.OSS.AccessKeySecret)
|
|
if err != nil {
|
|
fmt.Printf("FAIL 创建 OSS 客户端: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// 全域名 * 放行 (用户确认: top-fans-test 为测试桶, 预签名 URL 仍需签名才能访问)
|
|
//
|
|
// ⚠️ 重要: 阿里云 OSS 的 AllowedMethod 不支持 OPTIONS。
|
|
// 浏览器 CORS 预检的 OPTIONS 请求由 OSS 隐式处理(无需显式列出)。
|
|
// 加上 OPTIONS 会触发 400 InvalidArgument: "CORS Rule AllowedMethod Format Error"。
|
|
//
|
|
// Method 集合:
|
|
// GET - H5 fetch 拉 cutout / variant PNG (浏览器读图)
|
|
// HEAD - 上传后取文件 size
|
|
// POST - H5 PostObject 直传(uni-app H5 在浏览器跑时必须走 POST)
|
|
rule := oss.CORSRule{
|
|
AllowedOrigin: []string{"*"},
|
|
AllowedMethod: []string{"GET", "HEAD", "POST"},
|
|
AllowedHeader: []string{"*"},
|
|
ExposeHeader: []string{"ETag", "Content-Length", "Content-Type", "x-oss-request-id"},
|
|
MaxAgeSeconds: 3600,
|
|
}
|
|
|
|
// SDK v3.0.2: CORS 方法在 *Client 上
|
|
if err := client.SetBucketCORS(cfg.OSS.BucketName, []oss.CORSRule{rule}); err != nil {
|
|
fmt.Printf("FAIL SetBucketCORS: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// 立即读回验证
|
|
got, gErr := client.GetBucketCORS(cfg.OSS.BucketName)
|
|
if gErr != nil {
|
|
fmt.Printf("WARN GetBucketCORS 回读失败: %v\n", gErr)
|
|
} else {
|
|
fmt.Printf("OK 当前 bucket CORS 规则数: %d\n", len(got.CORSRules))
|
|
for i, r := range got.CORSRules {
|
|
fmt.Printf(" [%d] Origin=%v Method=%v Header=%v MaxAge=%d\n",
|
|
i, r.AllowedOrigin, r.AllowedMethod, r.AllowedHeader, r.MaxAgeSeconds)
|
|
}
|
|
}
|
|
|
|
fmt.Println("✅ CORS 配置已生效(可能有几秒传播延迟)")
|
|
}
|
|
|
|
func mask(s string) string {
|
|
if len(s) <= 8 {
|
|
return "***"
|
|
}
|
|
return s[:4] + "****" + s[len(s)-4:]
|
|
}
|