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"
|
2024-09-29 10:25:28 +08:00
|
|
|
"github.com/qiniu/go-sdk/v7/auth"
|
|
|
|
"github.com/qiniu/go-sdk/v7/cdn"
|
|
|
|
"github.com/qiniu/go-sdk/v7/storagev2/credentials"
|
|
|
|
"github.com/qiniu/go-sdk/v7/storagev2/downloader"
|
|
|
|
"github.com/qiniu/go-sdk/v7/storagev2/http_client"
|
|
|
|
"github.com/qiniu/go-sdk/v7/storagev2/objects"
|
|
|
|
"github.com/qiniu/go-sdk/v7/storagev2/uploader"
|
|
|
|
"github.com/qiniu/go-sdk/v7/storagev2/uptoken"
|
2024-07-23 10:23:43 +08:00
|
|
|
"github.com/tidwall/gjson"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"path"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2024-09-29 10:25:28 +08:00
|
|
|
type Qhash string
|
|
|
|
|
|
|
|
const (
|
|
|
|
QSha1 Qhash = "sha1"
|
|
|
|
QSha256 Qhash = "sha256"
|
|
|
|
)
|
|
|
|
|
2024-07-23 10:23:43 +08:00
|
|
|
var _ QiNiu = (*qiNiu)(nil)
|
|
|
|
|
|
|
|
type QiNiu interface {
|
2024-09-29 10:25:28 +08:00
|
|
|
SetDefaultUploadTokenTTL(duration time.Duration)
|
|
|
|
GetUploadToken() (string, error)
|
|
|
|
GetCallbackUploadToken(callbackURL string) (string, error)
|
|
|
|
TimestampSecuritySign(urlStr string, ttl time.Duration) (string, error)
|
|
|
|
UploadFile(key, localFile, callbackURL string) (*PutRet, error)
|
|
|
|
GetPrivateURL(key string, ttl time.Duration) (string, error)
|
2024-07-23 10:23:43 +08:00
|
|
|
VerifyCallback(req *http.Request) (bool, error)
|
2024-09-29 10:25:28 +08:00
|
|
|
GetFileInfo(key string) (*objects.ObjectDetails, error)
|
2024-07-23 10:23:43 +08:00
|
|
|
DelFile(key string) error
|
2024-09-29 10:25:28 +08:00
|
|
|
ListFiles(keyPrefix, marker string, limit uint64) ([]objects.ObjectDetails, error)
|
|
|
|
GetFileHash(path string, qhash Qhash) (hash string, err error)
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
type qiNiu struct {
|
|
|
|
bucket string
|
|
|
|
domain string
|
|
|
|
securityKey string
|
2024-09-29 10:25:28 +08:00
|
|
|
credentials *auth.Credentials
|
|
|
|
bucketManager *objects.Bucket
|
2024-07-23 10:23:43 +08:00
|
|
|
md5 md5.MD5
|
2024-09-29 10:25:28 +08:00
|
|
|
uploadTokenTTL time.Duration
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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-09-29 10:25:28 +08:00
|
|
|
cred := credentials.NewCredentials(accessKey, secretKey)
|
|
|
|
objectsManager := objects.NewObjectsManager(&objects.ObjectsManagerOptions{
|
|
|
|
Options: http_client.Options{Credentials: cred},
|
|
|
|
})
|
2024-07-23 10:23:43 +08:00
|
|
|
return &qiNiu{
|
|
|
|
bucket: bucket,
|
|
|
|
domain: domain,
|
|
|
|
securityKey: securityKey,
|
2024-09-29 10:25:28 +08:00
|
|
|
credentials: cred,
|
|
|
|
bucketManager: objectsManager.Bucket(bucket),
|
2024-07-23 10:23:43 +08:00
|
|
|
md5: md5.New(),
|
2024-09-29 10:25:28 +08:00
|
|
|
uploadTokenTTL: time.Second * 3600,
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-29 10:25:28 +08:00
|
|
|
func (q *qiNiu) SetDefaultUploadTokenTTL(duration time.Duration) {
|
|
|
|
q.uploadTokenTTL = duration
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
|
|
|
|
2024-09-29 10:25:28 +08:00
|
|
|
func (q *qiNiu) GetUploadToken() (string, error) {
|
|
|
|
putPolicy, err := uptoken.NewPutPolicy(q.bucket, time.Now().Add(q.uploadTokenTTL))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
2024-09-29 10:25:28 +08:00
|
|
|
return uptoken.NewSigner(putPolicy, q.credentials).GetUpToken(context.Background())
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
|
|
|
|
2024-09-29 10:25:28 +08:00
|
|
|
func (q *qiNiu) GetCallbackUploadToken(callbackURL string) (string, error) {
|
|
|
|
putPolicy, err := uptoken.NewPutPolicy(q.bucket, time.Now().Add(q.uploadTokenTTL))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
2024-09-29 10:25:28 +08:00
|
|
|
putPolicy.SetCallbackUrl(callbackURL).
|
|
|
|
SetCallbackBody(`{"key":"$(key)","hash":"$(etag)","fname":"$(fname)","fsize":"$(fsize)","ext":"$(ext)","unique":"$(x:unique)","user":"$(x:user)"}`).
|
|
|
|
SetCallbackBodyType("application/json")
|
|
|
|
return uptoken.NewSigner(putPolicy, q.credentials).GetUpToken(context.Background())
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
|
|
|
|
2024-09-29 10:25:28 +08:00
|
|
|
func (q *qiNiu) TimestampSecuritySign(urlStr string, ttl time.Duration) (string, error) {
|
|
|
|
deadline := time.Now().Add(ttl).Unix()
|
|
|
|
tUrl, err := cdn.CreateTimestampAntileechURL(urlStr, q.securityKey, deadline)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return tUrl, nil
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
|
|
|
|
2024-09-29 10:25:28 +08:00
|
|
|
func (q *qiNiu) UploadFile(key, localFile, callbackURL string) (*PutRet, error) {
|
|
|
|
putPolicy, err := uptoken.NewPutPolicy(q.bucket, time.Now().Add(q.uploadTokenTTL))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
putPolicy.SetCallbackUrl(callbackURL).
|
|
|
|
SetCallbackBody(`{"key":"$(key)","hash":"$(etag)","fname":"$(fname)","fsize":"$(fsize)","ext":"$(ext)","unique":"$(x:unique)","user":"$(x:user)"}`).
|
|
|
|
SetCallbackBodyType("application/json")
|
2024-07-23 10:23:43 +08:00
|
|
|
|
2024-09-29 10:25:28 +08:00
|
|
|
ret := &PutRet{}
|
2024-07-23 10:23:43 +08:00
|
|
|
filename := path.Base(key)
|
|
|
|
fileSuffix := path.Ext(key)
|
|
|
|
filePrefix := filename[0 : len(filename)-len(fileSuffix)]
|
2024-09-29 10:25:28 +08:00
|
|
|
uploadManager := uploader.NewUploadManager(&uploader.UploadManagerOptions{})
|
|
|
|
err = uploadManager.UploadFile(context.Background(), localFile, &uploader.ObjectOptions{
|
|
|
|
UpToken: uptoken.NewSigner(putPolicy, q.credentials),
|
|
|
|
ObjectName: &key,
|
|
|
|
CustomVars: map[string]string{
|
|
|
|
"unique": filePrefix,
|
|
|
|
"user": "api",
|
2024-07-23 10:23:43 +08:00
|
|
|
},
|
2024-09-29 10:25:28 +08:00
|
|
|
}, ret)
|
2024-07-23 10:23:43 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
2024-09-29 10:25:28 +08:00
|
|
|
func (q *qiNiu) GetPrivateURL(key string, ttl time.Duration) (string, error) {
|
|
|
|
urlsProvider := downloader.SignURLsProvider(
|
|
|
|
downloader.NewStaticDomainBasedURLsProvider([]string{q.domain}),
|
|
|
|
downloader.NewCredentialsSigner(q.credentials),
|
|
|
|
&downloader.SignOptions{
|
|
|
|
TTL: ttl,
|
|
|
|
})
|
|
|
|
iter, err := urlsProvider.GetURLsIter(context.Background(), key, &downloader.GenerateOptions{BucketName: q.bucket})
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
res := url.URL{}
|
|
|
|
peek, err := iter.Peek(&res)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
2024-09-29 10:25:28 +08:00
|
|
|
if peek {
|
|
|
|
return res.String(), nil
|
|
|
|
}
|
|
|
|
return "", errors.New("get private url false")
|
|
|
|
}
|
2024-07-23 10:23:43 +08:00
|
|
|
|
2024-09-29 10:25:28 +08:00
|
|
|
func (q *qiNiu) VerifyCallback(req *http.Request) (bool, error) {
|
|
|
|
return q.credentials.VerifyCallback(req)
|
|
|
|
}
|
2024-07-23 10:23:43 +08:00
|
|
|
|
2024-09-29 10:25:28 +08:00
|
|
|
func (q *qiNiu) GetFileInfo(key string) (*objects.ObjectDetails, error) {
|
|
|
|
objectInfo, err := q.bucketManager.Object(key).Stat().Call(context.Background())
|
2024-07-23 10:23:43 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-09-29 10:25:28 +08:00
|
|
|
return objectInfo, nil
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (q *qiNiu) DelFile(key string) error {
|
2024-09-29 10:25:28 +08:00
|
|
|
err := q.bucketManager.Object(key).Delete().Call(context.Background())
|
2024-07-23 10:23:43 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-09-29 10:25:28 +08:00
|
|
|
func (q *qiNiu) ListFiles(keyPrefix, marker string, limit uint64) ([]objects.ObjectDetails, error) {
|
|
|
|
iter := q.bucketManager.List(context.Background(), &objects.ListObjectsOptions{
|
|
|
|
Limit: &limit,
|
|
|
|
Prefix: keyPrefix,
|
|
|
|
Marker: marker,
|
|
|
|
})
|
|
|
|
defer func() {
|
|
|
|
_ = iter.Close()
|
|
|
|
}()
|
|
|
|
objectInfos := make([]objects.ObjectDetails, 0)
|
|
|
|
var objectInfo objects.ObjectDetails
|
|
|
|
for iter.Next(&objectInfo) {
|
|
|
|
objectInfos = append(objectInfos, objectInfo)
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
2024-09-29 10:25:28 +08:00
|
|
|
if err := iter.Error(); err != nil {
|
|
|
|
return nil, err
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
2024-09-29 10:25:28 +08:00
|
|
|
return objectInfos, nil
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
|
|
|
|
2024-09-29 10:25:28 +08:00
|
|
|
func (q *qiNiu) GetFileHash(path string, qhash Qhash) (hash string, err error) {
|
|
|
|
sign, err := q.TimestampSecuritySign(path, time.Second*5)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
2024-07-23 10:23:43 +08:00
|
|
|
}
|
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
|
|
|
|
}
|