2024-07-23 10:23:43 +08:00
|
|
|
package qiniu
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2024-07-31 16:49:14 +08:00
|
|
|
"gitea.bvbej.com/bvbej/base-golang/pkg/md5"
|
|
|
|
"gitea.bvbej.com/bvbej/base-golang/tool"
|
2024-07-23 10:23:43 +08:00
|
|
|
"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"`
|
|
|
|
}
|
|
|
|
|
2024-08-06 10:15:56 +08:00
|
|
|
func New(accessKey, secretKey, bucket, domain, securityKey string) QiNiu {
|
2024-07-23 10:23:43 +08:00
|
|
|
mac := qbox.NewMac(accessKey, secretKey)
|
|
|
|
conf := &storage.Config{
|
2024-08-06 10:15:56 +08:00
|
|
|
UseHTTPS: true, //是否使用https域名
|
|
|
|
UseCdnDomains: false, //上传是否使用CDN上传加速
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
|
|
|
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)
|
2024-08-06 10:04:41 +08:00
|
|
|
addr := fmt.Sprintf("%s/%s?%s&qhash/%s", strings.TrimRight(q.domain, "/"), path, sign, qhash)
|
2024-07-23 10:23:43 +08:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|