feat: minio

pull/818/head
withchao 2 years ago
parent cf815dcf71
commit 94be8fc0c4

@ -135,6 +135,7 @@ object:
sessionToken: "" sessionToken: ""
signEndpoint: "http://127.0.0.1:10005" signEndpoint: "http://127.0.0.1:10005"
thumbnailApi: "http://127.0.0.1:10003" thumbnailApi: "http://127.0.0.1:10003"
thumbnailUseSignEndpoint: false
cos: cos:
bucketURL: "https://temp-1252357374.cos.ap-chengdu.myqcloud.com" bucketURL: "https://temp-1252357374.cos.ap-chengdu.myqcloud.com"
secretID: "" secretID: ""

@ -37,7 +37,7 @@ require (
require github.com/google/uuid v1.3.0 require github.com/google/uuid v1.3.0
require ( require (
github.com/OpenIMSDK/protocol v0.0.3 github.com/OpenIMSDK/protocol v0.0.6
github.com/OpenIMSDK/tools v0.0.13 github.com/OpenIMSDK/tools v0.0.13
github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible
github.com/go-redis/redis v6.15.9+incompatible github.com/go-redis/redis v6.15.9+incompatible

@ -17,8 +17,8 @@ cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7Biccwk
firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4= firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4=
firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs= firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OpenIMSDK/protocol v0.0.3 h1:CFQtmnyW+1dYKVFaVaHcJ6oYuMiMdNfU2gC1xz3K/9I= github.com/OpenIMSDK/protocol v0.0.6 h1:KjaItOEww7vjrhwyxHnVzhw80pnjcNukpskadqW6gnA=
github.com/OpenIMSDK/protocol v0.0.3/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/OpenIMSDK/protocol v0.0.6/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
github.com/OpenIMSDK/tools v0.0.13 h1:rcw4HS8S2DPZR9UOBxD8/ol9UBMzXBypzOVEytDRIMo= github.com/OpenIMSDK/tools v0.0.13 h1:rcw4HS8S2DPZR9UOBxD8/ol9UBMzXBypzOVEytDRIMo=
github.com/OpenIMSDK/tools v0.0.13/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= github.com/OpenIMSDK/tools v0.0.13/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI=
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=

@ -81,7 +81,14 @@ func (o *ThirdApi) ObjectRedirect(c *gin.Context) {
operationID = strconv.Itoa(rand.Int()) operationID = strconv.Itoa(rand.Int())
} }
ctx := mcontext.SetOperationID(c, operationID) ctx := mcontext.SetOperationID(c, operationID)
resp, err := o.Client.AccessURL(ctx, &third.AccessURLReq{Name: name}) query := make(map[string]string)
for key, values := range c.Request.URL.Query() {
if len(values) == 0 {
continue
}
query[key] = values[0]
}
resp, err := o.Client.AccessURL(ctx, &third.AccessURLReq{Name: name, Query: query})
if err != nil { if err != nil {
if errs.ErrArgs.Is(err) { if errs.ErrArgs.Is(err) {
c.String(http.StatusBadRequest, err.Error()) c.String(http.StatusBadRequest, err.Error())

@ -16,6 +16,8 @@ package third
import ( import (
"context" "context"
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3"
"strconv"
"time" "time"
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3/cont" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3/cont"
@ -151,7 +153,22 @@ func (t *thirdServer) CompleteMultipartUpload(ctx context.Context, req *third.Co
} }
func (t *thirdServer) AccessURL(ctx context.Context, req *third.AccessURLReq) (*third.AccessURLResp, error) { func (t *thirdServer) AccessURL(ctx context.Context, req *third.AccessURLReq) (*third.AccessURLResp, error) {
expireTime, rawURL, err := t.s3dataBase.AccessURL(ctx, req.Name, t.defaultExpire) opt := &s3.AccessURLOption{}
if len(req.Query) > 0 {
switch req.Query["type"] {
case "image":
opt.Image.Format = req.Query["format"]
opt.Image.Width, _ = strconv.Atoi(req.Query["width"])
opt.Image.Height, _ = strconv.Atoi(req.Query["height"])
case "video":
opt.Video.Format = req.Query["format"]
opt.Video.Width, _ = strconv.Atoi(req.Query["width"])
opt.Video.Height, _ = strconv.Atoi(req.Query["height"])
millisecond, _ := strconv.Atoi(req.Query["time"])
opt.Video.Time = time.Millisecond * time.Duration(millisecond)
}
}
expireTime, rawURL, err := t.s3dataBase.AccessURL(ctx, req.Name, t.defaultExpire, opt)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -119,8 +119,9 @@ type configStruct struct {
AccessKeyID string `yaml:"accessKeyID"` AccessKeyID string `yaml:"accessKeyID"`
SecretAccessKey string `yaml:"secretAccessKey"` SecretAccessKey string `yaml:"secretAccessKey"`
SessionToken string `yaml:"sessionToken"` SessionToken string `yaml:"sessionToken"`
ThumbnailApi string `yaml:"thumbnailApi"`
SignEndpoint string `yaml:"signEndpoint"` SignEndpoint string `yaml:"signEndpoint"`
ThumbnailApi string `yaml:"thumbnailApi"`
ThumbnailUseSignEndpoint bool `yaml:"thumbnailUseSignEndpoint"`
} `yaml:"minio"` } `yaml:"minio"`
Cos struct { Cos struct {
BucketURL string `yaml:"bucketURL"` BucketURL string `yaml:"bucketURL"`

@ -30,7 +30,7 @@ type S3Database interface {
AuthSign(ctx context.Context, uploadID string, partNumbers []int) (*s3.AuthSignResult, error) AuthSign(ctx context.Context, uploadID string, partNumbers []int) (*s3.AuthSignResult, error)
InitiateMultipartUpload(ctx context.Context, hash string, size int64, expire time.Duration, maxParts int) (*cont.InitiateUploadResult, error) InitiateMultipartUpload(ctx context.Context, hash string, size int64, expire time.Duration, maxParts int) (*cont.InitiateUploadResult, error)
CompleteMultipartUpload(ctx context.Context, uploadID string, parts []string) (*cont.UploadResult, error) CompleteMultipartUpload(ctx context.Context, uploadID string, parts []string) (*cont.UploadResult, error)
AccessURL(ctx context.Context, name string, expire time.Duration) (time.Time, string, error) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (time.Time, string, error)
SetObject(ctx context.Context, info *relation.ObjectModel) error SetObject(ctx context.Context, info *relation.ObjectModel) error
} }
@ -70,14 +70,19 @@ func (s *s3Database) SetObject(ctx context.Context, info *relation.ObjectModel)
return s.obj.SetObject(ctx, info) return s.obj.SetObject(ctx, info)
} }
func (s *s3Database) AccessURL(ctx context.Context, name string, expire time.Duration) (time.Time, string, error) { func (s *s3Database) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (time.Time, string, error) {
obj, err := s.obj.Take(ctx, name) obj, err := s.obj.Take(ctx, name)
if err != nil { if err != nil {
return time.Time{}, "", err return time.Time{}, "", err
} }
opt := &s3.AccessURLOption{ if opt == nil {
ContentType: obj.ContentType, opt = &s3.AccessURLOption{}
Filename: filepath.Base(obj.Name), }
if opt.ContentType == "" {
opt.ContentType = obj.ContentType
}
if opt.Filename == "" {
opt.Filename = filepath.Base(obj.Name)
} }
expireTime := time.Now().Add(expire) expireTime := time.Now().Add(expire)
rawURL, err := s.s3.AccessURL(ctx, obj.Key, expire, opt) rawURL, err := s.s3.AccessURL(ctx, obj.Key, expire, opt)

@ -308,19 +308,19 @@ func (c *Cos) AccessURL(ctx context.Context, name string, expire time.Duration,
sec = 0 sec = 0
} }
query.Set("time", strconv.FormatFloat(sec, 'f', 3, 64)) query.Set("time", strconv.FormatFloat(sec, 'f', 3, 64))
switch opt.Video.ImageFormat { switch opt.Video.Format {
case case
videoSnapshotImagePng, videoSnapshotImagePng,
videoSnapshotImageJpg: videoSnapshotImageJpg:
default: default:
opt.Video.ImageFormat = videoSnapshotImageJpg opt.Video.Format = videoSnapshotImageJpg
} }
query.Set("format", opt.Video.ImageFormat) query.Set("format", opt.Video.Format)
opt.ContentType = "image/" + opt.Video.ImageFormat opt.ContentType = "image/" + opt.Video.Format
if opt.Filename == "" { if opt.Filename == "" {
opt.Filename = filepath.Base(name) + "." + opt.Video.ImageFormat opt.Filename = filepath.Base(name) + "." + opt.Video.Format
} else if filepath.Ext(opt.Filename) != "."+opt.Video.ImageFormat { } else if filepath.Ext(opt.Filename) != "."+opt.Video.Format {
opt.Filename += "." + opt.Video.ImageFormat opt.Filename += "." + opt.Video.Format
} }
if opt.Video.Width > 0 { if opt.Video.Width > 0 {
query.Set("width", strconv.Itoa(opt.Video.Width)) query.Set("width", strconv.Itoa(opt.Video.Width))

@ -71,6 +71,7 @@ func NewMinio() (s3.Interface, error) {
bucket: conf.Bucket, bucket: conf.Bucket,
bucketURL: conf.Endpoint + "/" + conf.Bucket + "/", bucketURL: conf.Endpoint + "/" + conf.Bucket + "/",
imageApi: imageApi, imageApi: imageApi,
imageUseSignAddr: conf.ThumbnailUseSignEndpoint,
core: &minio.Core{Client: client}, core: &minio.Core{Client: client},
lock: &sync.Mutex{}, lock: &sync.Mutex{},
init: false, init: false,
@ -103,6 +104,7 @@ type Minio struct {
bucket string bucket string
bucketURL string bucketURL string
imageApi string imageApi string
imageUseSignAddr bool
location string location string
opts *minio.Options opts *minio.Options
core *minio.Core core *minio.Core
@ -369,7 +371,13 @@ func (m *Minio) AccessURL(ctx context.Context, name string, expire time.Duration
} else if expire < time.Second { } else if expire < time.Second {
expire = time.Second expire = time.Second
} }
u, err := m.sign.PresignedGetObject(ctx, m.bucket, name, expire, reqParams) var client *minio.Client
if m.imageUseSignAddr {
client = m.sign
} else {
client = m.core.Client
}
u, err := client.PresignedGetObject(ctx, m.bucket, name, expire, reqParams)
if err != nil { if err != nil {
return "", err return "", err
} }

@ -291,9 +291,9 @@ func (o *OSS) AccessURL(ctx context.Context, name string, expire time.Duration,
} }
opt.ContentType = "image/" + format opt.ContentType = "image/" + format
if opt.Filename == "" { if opt.Filename == "" {
opt.Filename = filepath.Base(name) + "." + opt.Video.ImageFormat opt.Filename = filepath.Base(name) + "." + opt.Video.Format
} else if filepath.Ext(opt.Filename) != "."+opt.Video.ImageFormat { } else if filepath.Ext(opt.Filename) != "."+opt.Video.Format {
opt.Filename += "." + opt.Video.ImageFormat opt.Filename += "." + opt.Video.Format
} }
// https://oss-console-img-demo-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com/example.jpg?x-oss-process=image/resize,h_100,m_lfit // https://oss-console-img-demo-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com/example.jpg?x-oss-process=image/resize,h_100,m_lfit
process := "image/resize,m_lfit" process := "image/resize,m_lfit"
@ -313,18 +313,18 @@ func (o *OSS) AccessURL(ctx context.Context, name string, expire time.Duration,
if millisecond < 0 { if millisecond < 0 {
millisecond = 0 millisecond = 0
} }
switch opt.Video.ImageFormat { switch opt.Video.Format {
case videoSnapshotImageJpg, videoSnapshotImagePng: case videoSnapshotImageJpg, videoSnapshotImagePng:
default: default:
opt.Video.ImageFormat = videoSnapshotImageJpg opt.Video.Format = videoSnapshotImageJpg
} }
opt.ContentType = "image/" + opt.Video.ImageFormat opt.ContentType = "image/" + opt.Video.Format
if opt.Filename == "" { if opt.Filename == "" {
opt.Filename = filepath.Base(name) + "." + opt.Video.ImageFormat opt.Filename = filepath.Base(name) + "." + opt.Video.Format
} else if filepath.Ext(opt.Filename) != "."+opt.Video.ImageFormat { } else if filepath.Ext(opt.Filename) != "."+opt.Video.Format {
opt.Filename += "." + opt.Video.ImageFormat opt.Filename += "." + opt.Video.Format
} }
process := "video/snapshot,t_" + strconv.Itoa(millisecond) + ",f_" + opt.Video.ImageFormat process := "video/snapshot,t_" + strconv.Itoa(millisecond) + ",f_" + opt.Video.Format
if opt.Video.Width > 0 { if opt.Video.Width > 0 {
process += ",w_" + strconv.Itoa(opt.Video.Width) process += ",w_" + strconv.Itoa(opt.Video.Width)
} }

@ -126,7 +126,7 @@ type Video struct {
Width int `json:"width"` Width int `json:"width"`
Height int `json:"height"` Height int `json:"height"`
Time time.Duration `json:"time"` Time time.Duration `json:"time"`
ImageFormat string `json:"format"` Format string `json:"format"`
} }
type AccessURLOption struct { type AccessURLOption struct {

Loading…
Cancel
Save