// 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 }