storage.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. package qiniu
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "git.bvbej.com/bvbej/base-golang/pkg/md5"
  7. "git.bvbej.com/bvbej/base-golang/tool"
  8. "github.com/qiniu/go-sdk/v7/auth/qbox"
  9. "github.com/qiniu/go-sdk/v7/storage"
  10. "github.com/tidwall/gjson"
  11. "io"
  12. "net/http"
  13. "net/url"
  14. "path"
  15. "strings"
  16. "time"
  17. )
  18. var _ QiNiu = (*qiNiu)(nil)
  19. type QiNiu interface {
  20. i()
  21. SetDefaultUploadTokenTTL(ttl uint64)
  22. GetCallbackUploadToken(ttl uint64, callbackURL string) string
  23. GetUploadToken(ttl uint64) string
  24. GetPrivateURL(key string, ttl uint64) string
  25. VerifyCallback(req *http.Request) (bool, error)
  26. UploadFile(key, localFile string) (*PutRet, error)
  27. ResumeUploadFile(key, localFile string) (*PutRet, error)
  28. DelFile(key string) error
  29. TimestampSecuritySign(path string, ttl time.Duration) string
  30. GetFileInfo(key string) *storage.FileInfo
  31. ListFiles(prefix, delimiter, marker string, limit int) (entries []storage.ListItem, commonPrefixes []string, nextMarker string, hasNext bool, err error)
  32. GetFileHash(path, qhash string) (hash string, err error)
  33. }
  34. type qiNiu struct {
  35. mac *qbox.Mac
  36. bucketManager *storage.BucketManager
  37. conf *storage.Config
  38. bucket string
  39. domain string
  40. securityKey string
  41. md5 md5.MD5
  42. uploadTokenTTL uint64
  43. }
  44. type PutRet struct {
  45. Key string `json:"key"`
  46. Hash string `json:"hash"`
  47. Fsize string `json:"fsize"`
  48. Fname string `json:"fname"`
  49. Ext string `json:"ext"`
  50. Unique string `json:"unique"`
  51. User string `json:"user"`
  52. }
  53. func New(accessKey, secretKey, bucket, domain, securityKey string) QiNiu {
  54. region, _ := storage.GetRegion(accessKey, bucket)
  55. mac := qbox.NewMac(accessKey, secretKey)
  56. conf := &storage.Config{
  57. Region: region, //空间所在的存储区域
  58. UseHTTPS: true, //是否使用https域名
  59. UseCdnDomains: true, //上传是否使用CDN上传加速
  60. }
  61. return &qiNiu{
  62. mac: mac,
  63. bucketManager: storage.NewBucketManager(mac, conf),
  64. bucket: bucket,
  65. domain: domain,
  66. securityKey: securityKey,
  67. conf: conf,
  68. md5: md5.New(),
  69. uploadTokenTTL: 3600,
  70. }
  71. }
  72. func (q *qiNiu) i() {}
  73. func (q *qiNiu) SetDefaultUploadTokenTTL(ttl uint64) {
  74. q.uploadTokenTTL = ttl
  75. }
  76. func (q *qiNiu) GetUploadToken(ttl uint64) string {
  77. putPolicy := storage.PutPolicy{
  78. Scope: q.bucket,
  79. Expires: ttl,
  80. }
  81. return putPolicy.UploadToken(q.mac)
  82. }
  83. func (q *qiNiu) GetCallbackUploadToken(ttl uint64, callbackURL string) string {
  84. putPolicy := storage.PutPolicy{
  85. Scope: q.bucket,
  86. CallbackURL: callbackURL,
  87. CallbackBody: `{"key":"$(key)","hash":"$(etag)","fname":"$(fname)","fsize":"$(fsize)","ext":"$(ext)","unique":"$(x:unique)","user":"$(x:user)"}`,
  88. CallbackBodyType: "application/json",
  89. Expires: ttl,
  90. }
  91. return putPolicy.UploadToken(q.mac)
  92. }
  93. func (q *qiNiu) GetPrivateURL(key string, ttl uint64) string {
  94. deadline := time.Now().Add(time.Second * time.Duration(ttl)).Unix()
  95. return storage.MakePrivateURL(q.mac, q.domain, key, deadline)
  96. }
  97. func (q *qiNiu) VerifyCallback(req *http.Request) (bool, error) {
  98. return q.mac.VerifyCallback(req)
  99. }
  100. func (q *qiNiu) UploadFile(key, localFile string) (*PutRet, error) {
  101. upToken := q.GetUploadToken(q.uploadTokenTTL)
  102. //构建表单上传的对象
  103. formUploader := storage.NewFormUploader(q.conf)
  104. //请求参数
  105. filename := path.Base(key)
  106. fileSuffix := path.Ext(key)
  107. filePrefix := filename[0 : len(filename)-len(fileSuffix)]
  108. putExtra := &storage.PutExtra{
  109. Params: map[string]string{
  110. "x:unique": filePrefix,
  111. "x:user": "-",
  112. },
  113. }
  114. //自定义返回body
  115. ret := new(PutRet)
  116. err := formUploader.PutFile(context.Background(), ret, upToken, key, localFile, putExtra)
  117. if err != nil {
  118. return nil, err
  119. }
  120. return ret, nil
  121. }
  122. func (q *qiNiu) ResumeUploadFile(key, localFile string) (*PutRet, error) {
  123. upToken := q.GetUploadToken(q.uploadTokenTTL)
  124. //构建分片上传的对象
  125. resumeUploader := storage.NewResumeUploaderV2(q.conf)
  126. //请求参数
  127. filename := path.Base(key)
  128. fileSuffix := path.Ext(key)
  129. filePrefix := filename[0 : len(filename)-len(fileSuffix)]
  130. putExtra := &storage.RputV2Extra{
  131. CustomVars: map[string]string{
  132. "x:unique": filePrefix,
  133. "x:user": "-",
  134. },
  135. }
  136. //自定义返回body
  137. ret := new(PutRet)
  138. err := resumeUploader.PutFile(context.Background(), ret, upToken, key, localFile, putExtra)
  139. if err != nil {
  140. return nil, err
  141. }
  142. return ret, nil
  143. }
  144. func (q *qiNiu) DelFile(key string) error {
  145. err := q.bucketManager.Delete(q.bucket, key)
  146. if err != nil {
  147. return err
  148. }
  149. return nil
  150. }
  151. func (q *qiNiu) TimestampSecuritySign(path string, ttl time.Duration) string {
  152. sep := "/"
  153. path = strings.Trim(path, sep)
  154. splits := strings.Split(path, sep)
  155. for i, split := range splits {
  156. splits[i] = url.QueryEscape(split)
  157. }
  158. path = sep + strings.Join(splits, sep)
  159. unix := time.Now().Add(ttl).Unix()
  160. hex := fmt.Sprintf("%x", unix)
  161. encrypt := q.md5.Encrypt(q.securityKey + path + hex)
  162. param := make(url.Values)
  163. param.Set("sign", encrypt)
  164. param.Set("t", hex)
  165. return param.Encode()
  166. }
  167. func (q *qiNiu) GetFileInfo(key string) *storage.FileInfo {
  168. fileInfo, sErr := q.bucketManager.Stat(q.bucket, key)
  169. if sErr != nil {
  170. return nil
  171. }
  172. return &fileInfo
  173. }
  174. func (q *qiNiu) ListFiles(prefix, delimiter, marker string, limit int) (entries []storage.ListItem,
  175. commonPrefixes []string, nextMarker string, hasNext bool, err error) {
  176. return q.bucketManager.ListFiles(q.bucket, prefix, delimiter, marker, limit)
  177. }
  178. func (q *qiNiu) GetFileHash(path, qhash string) (hash string, err error) {
  179. if !tool.InArray(qhash, []string{"sha1", "md5", "sha256"}) {
  180. return "", errors.New("qhash invalid")
  181. }
  182. sign := q.TimestampSecuritySign(path, time.Second*5)
  183. addr := fmt.Sprintf("https://cdn.mogume.com/%s?%s&qhash/%s", path, sign, qhash)
  184. resp, err := http.Get(addr)
  185. defer func() { _ = resp.Body.Close() }()
  186. if err != nil {
  187. return "", err
  188. }
  189. if resp.StatusCode != http.StatusOK {
  190. return "", errors.New(resp.Status)
  191. }
  192. body, err := io.ReadAll(resp.Body)
  193. if err != nil {
  194. return "", err
  195. }
  196. return gjson.GetBytes(body, "hash").String(), nil
  197. }