package uploader import ( "bytes" "fmt" "image/png" "math/rand" "strings" "time" "github.com/aliyun/aliyun-oss-go-sdk/oss" "github.com/topfans/laserCompositor/config" ) // OSSUploader OSS 上传器 type OSSUploader struct { cfg config.OSSConfig } // NewOSSUploader 创建 OSS 上传器 func NewOSSUploader(cfg config.OSSConfig) *OSSUploader { return &OSSUploader{cfg: cfg} } // UploadResult 上传结果 type UploadResult struct { OSSKey string `json:"oss_key"` SignedURL string `json:"signed_url"` } // UploadPNG 上传 PNG 到 OSS,返回 oss_key 和 signed_url func (u *OSSUploader) UploadPNG(data []byte, dir, filename string) (*UploadResult, error) { bucket, err := u.getBucket() if err != nil { return nil, err } if dir == "" { dir = "laser-card/" } if filename == "" { filename = fmt.Sprintf("%d_%d.png", time.Now().UnixMilli(), rand.Intn(10000)) } ossKey := strings.TrimRight(dir, "/") + "/" + filename err = bucket.PutObject(ossKey, bytes.NewReader(data)) if err != nil { return nil, fmt.Errorf("OSS upload failed: %w", err) } // 生成签名 URL(1 小时有效) signedURL, err := bucket.SignURL(ossKey, oss.HTTPGet, 3600) if err != nil { return nil, fmt.Errorf("sign URL failed: %w", err) } return &UploadResult{ OSSKey: ossKey, SignedURL: signedURL, }, nil } // UploadPNG 变体:上传并返回带签名的 URL func (u *OSSUploader) getBucket() (*oss.Bucket, error) { ak := strings.TrimSpace(u.cfg.AccessKeyID) sk := strings.TrimSpace(u.cfg.AccessKeySecret) if ak == "" || sk == "" { return nil, fmt.Errorf("OSS credentials not configured") } endpoint := fmt.Sprintf("https://oss-%s.aliyuncs.com", u.cfg.Region) client, err := oss.New(endpoint, ak, sk) if err != nil { return nil, fmt.Errorf("create OSS client: %w", err) } bucket, err := client.Bucket(u.cfg.BucketName) if err != nil { return nil, fmt.Errorf("get bucket: %w", err) } return bucket, nil } // EncodePNG 将 compositor.ComposeResult 编码为 PNG bytes func EncodePNG(width, height int, data []byte) ([]byte, error) { // 如果 data 已经是完整的 PNG 数据,直接返回 if len(data) > 4 && data[0] == 0x89 && data[1] == 0x50 && data[2] == 0x4e && data[3] == 0x47 { return data, nil } var buf bytes.Buffer // 这里 data 预期是 raw RGBA pixels // 但实际上 compositor 返回的是 NRGBA image // 暂时返回空,由调用方在 handler 层处理 if err := png.Encode(&buf, nil); err != nil { return nil, err } return buf.Bytes(), nil }