upload.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. package service
  2. import (
  3. "bytes"
  4. "context"
  5. "crypto/hmac"
  6. "crypto/sha1"
  7. "encoding/base64"
  8. "fmt"
  9. "hash"
  10. "io/ioutil"
  11. "net/http"
  12. "strconv"
  13. "time"
  14. "go-common/app/admin/main/upload/model"
  15. "go-common/library/ecode"
  16. "go-common/library/log"
  17. "github.com/pkg/errors"
  18. )
  19. const (
  20. _downloadURL = "http://%s/bfs/%s/%s"
  21. _uploadURL = "http://%s/bfs/%s/%s"
  22. _deleteURL = "http://%s/bfs/%s/%s"
  23. _privateBucket = "facepri" // bucket to save yellow pic
  24. _privateBucketAppKey = "8923aff2e1124bb2"
  25. _privateBucketAppSecret = "b237e8927823cc2984aee980123cb0"
  26. )
  27. // Add will add a record into bfs_upload_admin table
  28. func (s *Service) Add(c context.Context, ap *model.AddParam) (err error) {
  29. record := new(model.Record)
  30. record.Bucket = ap.Bucket
  31. record.FileName = ap.FileName
  32. record.URL = ap.URL
  33. record.Sex = ap.Sex
  34. record.Politics = ap.Politics
  35. err = s.orm.Create(&record).Error
  36. return
  37. }
  38. // List lists records
  39. func (s *Service) List(c context.Context, lp *model.ListParam) (listResSlice []*model.Record, err error) {
  40. listResSlice = make([]*model.Record, 0)
  41. err = s.orm.Limit(10).Order("id desc").Where("state=?", lp.State).Where("bucket=?", lp.Bucket).Find(&listResSlice).Error
  42. return
  43. }
  44. // MultiList lists records from multi bucket
  45. func (s *Service) MultiList(c context.Context, lp *model.MultiListParam) (result []*model.MultiListResult, err error) {
  46. result = make([]*model.MultiListResult, 0)
  47. if len(lp.Bucket) == 0 {
  48. var buckets []*model.Bucket
  49. if err = s.orm.Table("bucket").Order("id desc").Find(&buckets).Error; err != nil {
  50. log.Error("read bucket error(%v)", err)
  51. return
  52. }
  53. for _, v := range buckets {
  54. lp.Bucket = append(lp.Bucket, v.BucketName)
  55. }
  56. }
  57. for _, bucket := range lp.Bucket {
  58. tmpResult := &model.MultiListResult{}
  59. tmpResult.Bucket = bucket
  60. tmpRecord := make([]*model.Record, 0)
  61. if err = s.orm.Limit(10).Order("id desc").Where("state=?", lp.State).Where("bucket=?", bucket).Find(&tmpRecord).Error; err != nil {
  62. return
  63. }
  64. tmpResult.Imgs = tmpRecord
  65. result = append(result, tmpResult)
  66. }
  67. return
  68. }
  69. // Delete deletes a record and delete file in bfs
  70. func (s *Service) Delete(c context.Context, dp *model.DeleteParam) (err error) {
  71. var (
  72. downloadBytes []byte
  73. contentType string
  74. )
  75. record := new(model.Record)
  76. if err = s.orm.Where("id=?", dp.Rid).Find(&record).Error; err != nil {
  77. err = errors.Wrapf(err, "Query(%d)", dp.Rid)
  78. return
  79. }
  80. dp.Bucket = record.Bucket
  81. dp.FileName = record.FileName
  82. if downloadBytes, contentType, err = s.download(dp); err != nil {
  83. return
  84. }
  85. if err = s.upload(dp, contentType, downloadBytes); err != nil {
  86. return
  87. }
  88. if err = s.delete(dp); err != nil {
  89. return
  90. }
  91. if err = s.orm.Where("id=?", dp.Rid).Update("state", 1).Update("adminid", dp.AdminID).Error; err != nil {
  92. err = errors.Wrapf(err, "Update(%d,%d,%d)", dp.Rid, 1, dp.AdminID)
  93. return
  94. }
  95. return
  96. }
  97. // DeleteRaw delete file in bfs
  98. func (s *Service) DeleteRaw(c context.Context, dp *model.DeleteRawParam) (err error) {
  99. d := &model.DeleteParam{
  100. Bucket: dp.Bucket,
  101. FileName: dp.FileName,
  102. }
  103. return s.delete(d)
  104. }
  105. // download from bfs
  106. func (s *Service) download(dp *model.DeleteParam) (downloadBytes []byte, contentType string, err error) {
  107. var (
  108. downloadReq *http.Request
  109. resp *http.Response
  110. bfsDownloadURL string
  111. )
  112. client := &http.Client{
  113. Timeout: time.Duration(s.c.HTTPClient.Read.Timeout),
  114. }
  115. bfsDownloadURL = fmt.Sprintf(_downloadURL, s.c.BfsDownloadHost, dp.Bucket, dp.FileName)
  116. if downloadReq, err = http.NewRequest(http.MethodGet, bfsDownloadURL, nil); err != nil {
  117. log.Error("client.NewRequest(%s) error(%v)", bfsDownloadURL, err)
  118. return
  119. }
  120. if resp, err = client.Do(downloadReq); err != nil {
  121. log.Error("client.Do(%v) error(%v)", downloadReq, err)
  122. return
  123. }
  124. contentType = resp.Header.Get("Content-Type")
  125. if downloadBytes, err = ioutil.ReadAll(resp.Body); err != nil {
  126. log.Error("ioutil.ReadAll(%v) error(%v)", resp.Body, err)
  127. return
  128. }
  129. return
  130. }
  131. // upload file to facepri bucket
  132. func (s *Service) upload(dp *model.DeleteParam, contentType string, body []byte) (err error) {
  133. var (
  134. uploadReq *http.Request
  135. resp *http.Response
  136. bfsUploadURL string
  137. )
  138. client := &http.Client{
  139. Timeout: time.Duration(s.c.HTTPClient.Read.Timeout),
  140. }
  141. bfsUploadURL = fmt.Sprintf(_uploadURL, s.c.BfsUpdateHost, dp.Bucket, dp.FileName)
  142. if uploadReq, err = http.NewRequest(http.MethodPut, bfsUploadURL, bytes.NewReader(body)); err != nil {
  143. return
  144. }
  145. auth := s.authorize(_privateBucketAppKey, _privateBucketAppSecret, http.MethodPut, _privateBucket, dp.FileName, time.Now().Unix())
  146. uploadReq.Header.Add("Host", "bfs.bilibili.co")
  147. uploadReq.Header.Add("Date", time.Now().String())
  148. uploadReq.Header.Add("Authorization", auth)
  149. uploadReq.Header.Add("Content-Type", contentType)
  150. uploadReq.Header.Add("Date", fmt.Sprint(time.Now().Unix()))
  151. if resp, err = client.Do(uploadReq); err != nil {
  152. log.Error("client.Do(%v) error(%v)", uploadReq, err)
  153. return
  154. }
  155. // judge response code
  156. switch resp.StatusCode {
  157. case http.StatusOK:
  158. case http.StatusBadRequest:
  159. err = ecode.RequestErr
  160. return
  161. case http.StatusUnauthorized:
  162. // 验证不通过
  163. err = ecode.BfsUploadAuthErr
  164. return
  165. case http.StatusRequestEntityTooLarge:
  166. err = ecode.FileTooLarge
  167. return
  168. case http.StatusNotFound:
  169. err = ecode.NothingFound
  170. return
  171. case http.StatusMethodNotAllowed:
  172. err = ecode.MethodNotAllowed
  173. return
  174. case http.StatusServiceUnavailable:
  175. err = ecode.BfsUploadServiceUnavailable
  176. return
  177. case http.StatusInternalServerError:
  178. err = ecode.ServerErr
  179. return
  180. default:
  181. err = ecode.BfsUploadStatusErr
  182. return
  183. }
  184. code, err := strconv.Atoi(resp.Header.Get("code"))
  185. if err != nil || code != 200 {
  186. err = ecode.BfsUploadCodeErr
  187. return
  188. }
  189. return
  190. }
  191. // delete file in old bucket
  192. func (s *Service) delete(dp *model.DeleteParam) (err error) {
  193. var (
  194. deleteReq *http.Request
  195. resp *http.Response
  196. bfsDeleteURL string
  197. )
  198. client := &http.Client{
  199. Timeout: time.Duration(s.c.HTTPClient.Read.Timeout),
  200. }
  201. bfsDeleteURL = fmt.Sprintf(_deleteURL, s.c.BfsDeleteHost, dp.Bucket, dp.FileName)
  202. if deleteReq, err = http.NewRequest("DELETE", bfsDeleteURL, nil); err != nil {
  203. log.Error("client.NewRequest(%s) error(%v)", bfsDeleteURL, err)
  204. return
  205. }
  206. item, ok := s.bucketCache[dp.Bucket]
  207. if !ok {
  208. err = errors.Wrapf(ecode.NothingFound, "bucket not exist: %s", dp.Bucket)
  209. log.Error("bucket not exist: %s", dp.Bucket)
  210. return
  211. }
  212. deleteReq.Header.Add("Host", "bfs.bilibili.co")
  213. deleteReq.Header.Add("Date", fmt.Sprint(time.Now().Unix()))
  214. deleteReq.Header.Add("Authorization", s.authorize(item.KeyID, item.KeySecret, http.MethodDelete, dp.Bucket, dp.FileName, time.Now().Unix()))
  215. if resp, err = client.Do(deleteReq); err != nil {
  216. log.Error("client.Do(%v) error(%v)", deleteReq, err)
  217. return
  218. }
  219. if resp.StatusCode != 200 {
  220. log.Error("bfs delete error code: %d", resp.StatusCode)
  221. return
  222. }
  223. return
  224. }
  225. // authorize return token
  226. func (s *Service) authorize(key, secret, method, bucket, fileName string, expire int64) (authorization string) {
  227. var (
  228. content string
  229. mac hash.Hash
  230. signature string
  231. )
  232. content = fmt.Sprintf("%s\n%s\n%s\n%d\n", method, bucket, fileName, expire)
  233. mac = hmac.New(sha1.New, []byte(secret))
  234. mac.Write([]byte(content))
  235. signature = base64.StdEncoding.EncodeToString(mac.Sum(nil))
  236. authorization = fmt.Sprintf("%s:%s:%d", key, signature, expire)
  237. return
  238. }
  239. // DeleteV2 deletes a record and delete file in bfs
  240. func (s *Service) DeleteV2(c context.Context, dp *model.DeleteV2Param) (err error) {
  241. switch dp.Status {
  242. case model.PassStatus:
  243. if err = s.orm.Table("upload_yellowing").Where("id=?", dp.Rid).Update("state", model.PassStatus).Update("adminid", dp.AdminID).Error; err != nil {
  244. err = errors.Wrapf(err, "Update(%d,%d,%d)", dp.Rid, model.PassStatus, dp.AdminID)
  245. return
  246. }
  247. case model.DeleteStatus:
  248. record := new(model.Record)
  249. if err = s.orm.Table("upload_yellowing").Where("id=?", dp.Rid).Find(&record).Error; err != nil {
  250. err = errors.Wrapf(err, "Query(%d)", dp.Rid)
  251. return
  252. }
  253. dp.Bucket = record.Bucket
  254. dp.FileName = record.FileName
  255. if err = s.delete(&model.DeleteParam{
  256. Bucket: dp.Bucket,
  257. FileName: dp.FileName,
  258. }); err != nil {
  259. return
  260. }
  261. if err = s.orm.Table("upload_yellowing").Where("id=?", dp.Rid).Update("state", model.DeleteStatus).Update("adminid", dp.AdminID).Error; err != nil {
  262. err = errors.Wrapf(err, "Update(%d,%d,%d)", dp.Rid, model.DeleteStatus, dp.AdminID)
  263. return
  264. }
  265. default:
  266. err = errors.Wrapf(err, "illegal Status(%d,%d,%d)", dp.Rid, model.DeleteStatus, dp.AdminID)
  267. return
  268. }
  269. return
  270. }