first commit
This commit is contained in:
236
pkg/qiniu/storage.go
Normal file
236
pkg/qiniu/storage.go
Normal file
@ -0,0 +1,236 @@
|
||||
package qiniu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/md5"
|
||||
"git.bvbej.com/bvbej/base-golang/tool"
|
||||
"github.com/qiniu/go-sdk/v7/auth/qbox"
|
||||
"github.com/qiniu/go-sdk/v7/storage"
|
||||
"github.com/tidwall/gjson"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ QiNiu = (*qiNiu)(nil)
|
||||
|
||||
type QiNiu interface {
|
||||
i()
|
||||
SetDefaultUploadTokenTTL(ttl uint64)
|
||||
GetCallbackUploadToken(ttl uint64, callbackURL string) string
|
||||
GetUploadToken(ttl uint64) string
|
||||
GetPrivateURL(key string, ttl uint64) string
|
||||
VerifyCallback(req *http.Request) (bool, error)
|
||||
UploadFile(key, localFile string) (*PutRet, error)
|
||||
ResumeUploadFile(key, localFile string) (*PutRet, error)
|
||||
DelFile(key string) error
|
||||
TimestampSecuritySign(path string, ttl time.Duration) string
|
||||
GetFileInfo(key string) *storage.FileInfo
|
||||
ListFiles(prefix, delimiter, marker string, limit int) (entries []storage.ListItem, commonPrefixes []string, nextMarker string, hasNext bool, err error)
|
||||
GetFileHash(path, qhash string) (hash string, err error)
|
||||
}
|
||||
|
||||
type qiNiu struct {
|
||||
mac *qbox.Mac
|
||||
bucketManager *storage.BucketManager
|
||||
conf *storage.Config
|
||||
bucket string
|
||||
domain string
|
||||
securityKey string
|
||||
md5 md5.MD5
|
||||
uploadTokenTTL uint64
|
||||
}
|
||||
|
||||
type PutRet struct {
|
||||
Key string `json:"key"`
|
||||
Hash string `json:"hash"`
|
||||
Fsize string `json:"fsize"`
|
||||
Fname string `json:"fname"`
|
||||
Ext string `json:"ext"`
|
||||
Unique string `json:"unique"`
|
||||
User string `json:"user"`
|
||||
}
|
||||
|
||||
func New(accessKey, secretKey, bucket, domain, securityKey string) QiNiu {
|
||||
region, _ := storage.GetRegion(accessKey, bucket)
|
||||
mac := qbox.NewMac(accessKey, secretKey)
|
||||
conf := &storage.Config{
|
||||
Region: region, //空间所在的存储区域
|
||||
UseHTTPS: true, //是否使用https域名
|
||||
UseCdnDomains: true, //上传是否使用CDN上传加速
|
||||
}
|
||||
return &qiNiu{
|
||||
mac: mac,
|
||||
bucketManager: storage.NewBucketManager(mac, conf),
|
||||
bucket: bucket,
|
||||
domain: domain,
|
||||
securityKey: securityKey,
|
||||
conf: conf,
|
||||
md5: md5.New(),
|
||||
uploadTokenTTL: 3600,
|
||||
}
|
||||
}
|
||||
|
||||
func (q *qiNiu) i() {}
|
||||
|
||||
func (q *qiNiu) SetDefaultUploadTokenTTL(ttl uint64) {
|
||||
q.uploadTokenTTL = ttl
|
||||
}
|
||||
|
||||
func (q *qiNiu) GetUploadToken(ttl uint64) string {
|
||||
putPolicy := storage.PutPolicy{
|
||||
Scope: q.bucket,
|
||||
Expires: ttl,
|
||||
}
|
||||
return putPolicy.UploadToken(q.mac)
|
||||
}
|
||||
|
||||
func (q *qiNiu) GetCallbackUploadToken(ttl uint64, callbackURL string) string {
|
||||
putPolicy := storage.PutPolicy{
|
||||
Scope: q.bucket,
|
||||
CallbackURL: callbackURL,
|
||||
CallbackBody: `{"key":"$(key)","hash":"$(etag)","fname":"$(fname)","fsize":"$(fsize)","ext":"$(ext)","unique":"$(x:unique)","user":"$(x:user)"}`,
|
||||
CallbackBodyType: "application/json",
|
||||
Expires: ttl,
|
||||
}
|
||||
return putPolicy.UploadToken(q.mac)
|
||||
}
|
||||
|
||||
func (q *qiNiu) GetPrivateURL(key string, ttl uint64) string {
|
||||
deadline := time.Now().Add(time.Second * time.Duration(ttl)).Unix()
|
||||
return storage.MakePrivateURL(q.mac, q.domain, key, deadline)
|
||||
}
|
||||
|
||||
func (q *qiNiu) VerifyCallback(req *http.Request) (bool, error) {
|
||||
return q.mac.VerifyCallback(req)
|
||||
}
|
||||
|
||||
func (q *qiNiu) UploadFile(key, localFile string) (*PutRet, error) {
|
||||
upToken := q.GetUploadToken(q.uploadTokenTTL)
|
||||
|
||||
//构建表单上传的对象
|
||||
formUploader := storage.NewFormUploader(q.conf)
|
||||
|
||||
//请求参数
|
||||
filename := path.Base(key)
|
||||
fileSuffix := path.Ext(key)
|
||||
filePrefix := filename[0 : len(filename)-len(fileSuffix)]
|
||||
putExtra := &storage.PutExtra{
|
||||
Params: map[string]string{
|
||||
"x:unique": filePrefix,
|
||||
"x:user": "-",
|
||||
},
|
||||
}
|
||||
|
||||
//自定义返回body
|
||||
ret := new(PutRet)
|
||||
|
||||
err := formUploader.PutFile(context.Background(), ret, upToken, key, localFile, putExtra)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (q *qiNiu) ResumeUploadFile(key, localFile string) (*PutRet, error) {
|
||||
upToken := q.GetUploadToken(q.uploadTokenTTL)
|
||||
|
||||
//构建分片上传的对象
|
||||
resumeUploader := storage.NewResumeUploaderV2(q.conf)
|
||||
|
||||
//请求参数
|
||||
filename := path.Base(key)
|
||||
fileSuffix := path.Ext(key)
|
||||
filePrefix := filename[0 : len(filename)-len(fileSuffix)]
|
||||
putExtra := &storage.RputV2Extra{
|
||||
CustomVars: map[string]string{
|
||||
"x:unique": filePrefix,
|
||||
"x:user": "-",
|
||||
},
|
||||
}
|
||||
|
||||
//自定义返回body
|
||||
ret := new(PutRet)
|
||||
|
||||
err := resumeUploader.PutFile(context.Background(), ret, upToken, key, localFile, putExtra)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (q *qiNiu) DelFile(key string) error {
|
||||
err := q.bucketManager.Delete(q.bucket, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *qiNiu) TimestampSecuritySign(path string, ttl time.Duration) string {
|
||||
sep := "/"
|
||||
path = strings.Trim(path, sep)
|
||||
splits := strings.Split(path, sep)
|
||||
for i, split := range splits {
|
||||
splits[i] = url.QueryEscape(split)
|
||||
}
|
||||
path = sep + strings.Join(splits, sep)
|
||||
|
||||
unix := time.Now().Add(ttl).Unix()
|
||||
hex := fmt.Sprintf("%x", unix)
|
||||
|
||||
encrypt := q.md5.Encrypt(q.securityKey + path + hex)
|
||||
|
||||
param := make(url.Values)
|
||||
param.Set("sign", encrypt)
|
||||
param.Set("t", hex)
|
||||
|
||||
return param.Encode()
|
||||
}
|
||||
|
||||
func (q *qiNiu) GetFileInfo(key string) *storage.FileInfo {
|
||||
fileInfo, sErr := q.bucketManager.Stat(q.bucket, key)
|
||||
if sErr != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &fileInfo
|
||||
}
|
||||
|
||||
func (q *qiNiu) ListFiles(prefix, delimiter, marker string, limit int) (entries []storage.ListItem,
|
||||
commonPrefixes []string, nextMarker string, hasNext bool, err error) {
|
||||
return q.bucketManager.ListFiles(q.bucket, prefix, delimiter, marker, limit)
|
||||
}
|
||||
|
||||
func (q *qiNiu) GetFileHash(path, qhash string) (hash string, err error) {
|
||||
if !tool.InArray(qhash, []string{"sha1", "md5", "sha256"}) {
|
||||
return "", errors.New("qhash invalid")
|
||||
}
|
||||
|
||||
sign := q.TimestampSecuritySign(path, time.Second*5)
|
||||
addr := fmt.Sprintf("https://cdn.mogume.com/%s?%s&qhash/%s", path, sign, qhash)
|
||||
|
||||
resp, err := http.Get(addr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", errors.New(resp.Status)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return gjson.GetBytes(body, "hash").String(), nil
|
||||
}
|
Reference in New Issue
Block a user