You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

408 lines
13 KiB

5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
4 years ago
5 years ago
4 years ago
4 years ago
5 years ago
4 years ago
5 years ago
4 years ago
5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
5 years ago
4 years ago
5 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. package cos
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/base64"
  6. "encoding/json"
  7. "encoding/xml"
  8. "errors"
  9. "fmt"
  10. "hash/crc64"
  11. "io"
  12. "net/http"
  13. "os"
  14. "strconv"
  15. )
  16. type CIService service
  17. type PicOperations struct {
  18. IsPicInfo int `json:"is_pic_info,omitempty"`
  19. Rules []PicOperationsRules `json:"rules,omitemtpy"`
  20. }
  21. type PicOperationsRules struct {
  22. Bucket string `json:"bucket,omitempty"`
  23. FileId string `json:"fileid"`
  24. Rule string `json:"rule"`
  25. }
  26. func EncodePicOperations(pic *PicOperations) string {
  27. if pic == nil {
  28. return ""
  29. }
  30. bs, err := json.Marshal(pic)
  31. if err != nil {
  32. return ""
  33. }
  34. return string(bs)
  35. }
  36. type ImageProcessResult struct {
  37. XMLName xml.Name `xml:"UploadResult"`
  38. OriginalInfo *PicOriginalInfo `xml:"OriginalInfo,omitempty"`
  39. ProcessResults *PicProcessObject `xml:"ProcessResults>Object,omitempty"`
  40. }
  41. type PicOriginalInfo struct {
  42. Key string `xml:"Key,omitempty"`
  43. Location string `xml:"Location,omitempty"`
  44. ImageInfo *PicImageInfo `xml:"ImageInfo,omitempty"`
  45. ETag string `xml:"ETag,omitempty"`
  46. }
  47. type PicImageInfo struct {
  48. Format string `xml:"Format,omitempty"`
  49. Width int `xml:"Width,omitempty"`
  50. Height int `xml:"Height,omitempty"`
  51. Quality int `xml:"Quality,omitempty"`
  52. Ave string `xml:"Ave,omitempty"`
  53. Orientation int `xml:"Orientation,omitempty"`
  54. }
  55. type PicProcessObject struct {
  56. Key string `xml:"Key,omitempty"`
  57. Location string `xml:"Location,omitempty"`
  58. Format string `xml:"Format,omitempty"`
  59. Width int `xml:"Width,omitempty"`
  60. Height int `xml:"Height,omitempty"`
  61. Size int `xml:"Size,omitempty"`
  62. Quality int `xml:"Quality,omitempty"`
  63. ETag string `xml:"ETag,omitempty"`
  64. WatermarkStatus int `xml:"WatermarkStatus,omitempty"`
  65. CodeStatus int `xml:"CodeStatus,omitempty"`
  66. QRcodeInfo []QRcodeInfo `xml:"QRcodeInfo,omitempty"`
  67. }
  68. type QRcodeInfo struct {
  69. CodeUrl string `xml:"CodeUrl,omitempty"`
  70. CodeLocation *CodeLocation `xml:"CodeLocation,omitempty"`
  71. }
  72. type CodeLocation struct {
  73. Point []string `xml:"Point,omitempty"`
  74. }
  75. type picOperationsHeader struct {
  76. PicOperations string `header:"Pic-Operations" xml:"-" url:"-"`
  77. }
  78. type ImageProcessOptions = PicOperations
  79. // 云上数据处理 https://cloud.tencent.com/document/product/460/18147
  80. func (s *CIService) ImageProcess(ctx context.Context, name string, opt *ImageProcessOptions) (*ImageProcessResult, *Response, error) {
  81. header := &picOperationsHeader{
  82. PicOperations: EncodePicOperations(opt),
  83. }
  84. var res ImageProcessResult
  85. sendOpt := sendOptions{
  86. baseURL: s.client.BaseURL.BucketURL,
  87. uri: "/" + encodeURIComponent(name) + "?image_process",
  88. method: http.MethodPost,
  89. optHeader: header,
  90. result: &res,
  91. }
  92. resp, err := s.client.send(ctx, &sendOpt)
  93. return &res, resp, err
  94. }
  95. type ImageRecognitionOptions struct {
  96. CIProcess string `url:"ci-process,omitempty"`
  97. DetectType string `url:"detect-type,omitempty"`
  98. }
  99. type ImageRecognitionResult struct {
  100. XMLName xml.Name `xml:"RecognitionResult"`
  101. PornInfo *RecognitionInfo `xml:"PornInfo,omitempty"`
  102. TerroristInfo *RecognitionInfo `xml:"TerroristInfo,omitempty"`
  103. PoliticsInfo *RecognitionInfo `xml:"PoliticsInfo,omitempty"`
  104. AdsInfo *RecognitionInfo `xml:"AdsInfo,omitempty"`
  105. }
  106. type RecognitionInfo struct {
  107. Code int `xml:"Code,omitempty"`
  108. Msg string `xml:"Msg,omitempty"`
  109. HitFlag int `xml:"HitFlag,omitempty"`
  110. Score int `xml:"Score,omitempty"`
  111. Label string `xml:"Label,omitempty"`
  112. Count int `xml:"Count,omitempty"`
  113. }
  114. // 图片审核 https://cloud.tencent.com/document/product/460/37318
  115. func (s *CIService) ImageRecognition(ctx context.Context, name string, DetectType string) (*ImageRecognitionResult, *Response, error) {
  116. opt := &ImageRecognitionOptions{
  117. CIProcess: "sensitive-content-recognition",
  118. DetectType: DetectType,
  119. }
  120. var res ImageRecognitionResult
  121. sendOpt := sendOptions{
  122. baseURL: s.client.BaseURL.BucketURL,
  123. uri: "/" + encodeURIComponent(name),
  124. method: http.MethodGet,
  125. optQuery: opt,
  126. result: &res,
  127. }
  128. resp, err := s.client.send(ctx, &sendOpt)
  129. return &res, resp, err
  130. }
  131. type PutVideoAuditingJobOptions struct {
  132. XMLName xml.Name `xml:"Request"`
  133. InputObject string `xml:"Input>Object"`
  134. Conf *VideoAuditingJobConf `xml:"Conf"`
  135. }
  136. type VideoAuditingJobConf struct {
  137. DetectType string `xml:",omitempty"`
  138. Snapshot *PutVideoAuditingJobSnapshot `xml:",omitempty"`
  139. Callback string `xml:",omitempty"`
  140. }
  141. type PutVideoAuditingJobSnapshot struct {
  142. Mode string `xml:",omitempty"`
  143. Count int `xml:",omitempty"`
  144. TimeInterval float32 `xml:",omitempty"`
  145. Start float32 `xml:",omitempty"`
  146. }
  147. type PutVideoAuditingJobResult struct {
  148. XMLName xml.Name `xml:"Response"`
  149. JobsDetail struct {
  150. JobId string `xml:"JobId,omitempty"`
  151. State string `xml:"State,omitempty"`
  152. CreationTime string `xml:"CreationTime,omitempty"`
  153. Object string `xml:"Object,omitempty"`
  154. } `xml:"JobsDetail,omitempty"`
  155. }
  156. // 视频审核-创建任务 https://cloud.tencent.com/document/product/460/46427
  157. func (s *CIService) PutVideoAuditingJob(ctx context.Context, opt *PutVideoAuditingJobOptions) (*PutVideoAuditingJobResult, *Response, error) {
  158. var res PutVideoAuditingJobResult
  159. sendOpt := sendOptions{
  160. baseURL: s.client.BaseURL.CIURL,
  161. uri: "/video/auditing",
  162. method: http.MethodPost,
  163. body: opt,
  164. result: &res,
  165. }
  166. resp, err := s.client.send(ctx, &sendOpt)
  167. return &res, resp, err
  168. }
  169. type GetVideoAuditingJobResult struct {
  170. XMLName xml.Name `xml:"Response"`
  171. JobsDetail *VideoAuditingJobDetail `xml:",omitempty"`
  172. NonExistJobIds string `xml:",omitempty"`
  173. }
  174. type VideoAuditingJobDetail struct {
  175. Code string `xml:",omitempty"`
  176. Message string `xml:",omitempty"`
  177. JobId string `xml:",omitempty"`
  178. State string `xml:",omitempty"`
  179. CreationTime string `xml:",omitempty"`
  180. Object string `xml:",omitempty"`
  181. SnapshotCount string `xml:",omitempty"`
  182. Result int `xml:",omitempty"`
  183. PornInfo *RecognitionInfo `xml:",omitempty"`
  184. TerrorismInfo *RecognitionInfo `xml:",omitempty"`
  185. PoliticsInfo *RecognitionInfo `xml:",omitempty"`
  186. AdsInfo *RecognitionInfo `xml:",omitempty"`
  187. Snapshot *GetVideoAuditingJobSnapshot `xml:",omitempty"`
  188. }
  189. type GetVideoAuditingJobSnapshot struct {
  190. Url string `xml:",omitempty"`
  191. PornInfo *RecognitionInfo `xml:",omitempty"`
  192. TerrorismInfo *RecognitionInfo `xml:",omitempty"`
  193. PoliticsInfo *RecognitionInfo `xml:",omitempty"`
  194. AdsInfo *RecognitionInfo `xml:",omitempty"`
  195. }
  196. // 视频审核-查询任务 https://cloud.tencent.com/document/product/460/46926
  197. func (s *CIService) GetVideoAuditingJob(ctx context.Context, jobid string) (*GetVideoAuditingJobResult, *Response, error) {
  198. var res GetVideoAuditingJobResult
  199. sendOpt := sendOptions{
  200. baseURL: s.client.BaseURL.CIURL,
  201. uri: "/video/auditing/" + jobid,
  202. method: http.MethodGet,
  203. result: &res,
  204. }
  205. resp, err := s.client.send(ctx, &sendOpt)
  206. return &res, resp, err
  207. }
  208. // 图片持久化处理-上传时处理 https://cloud.tencent.com/document/product/460/18147
  209. // 二维码识别-上传时识别 https://cloud.tencent.com/document/product/460/37513
  210. func (s *CIService) Put(ctx context.Context, name string, r io.Reader, uopt *ObjectPutOptions) (*ImageProcessResult, *Response, error) {
  211. if r == nil {
  212. return nil, nil, fmt.Errorf("reader is nil")
  213. }
  214. if err := CheckReaderLen(r); err != nil {
  215. return nil, nil, err
  216. }
  217. opt := cloneObjectPutOptions(uopt)
  218. totalBytes, err := GetReaderLen(r)
  219. if err != nil && opt != nil && opt.Listener != nil {
  220. return nil, nil, err
  221. }
  222. if err == nil {
  223. // 与 go http 保持一致, 非bytes.Buffer/bytes.Reader/strings.Reader由用户指定ContentLength, 或使用 Chunk 上传
  224. if opt != nil && opt.ContentLength == 0 && IsLenReader(r) {
  225. opt.ContentLength = totalBytes
  226. }
  227. }
  228. reader := TeeReader(r, nil, totalBytes, nil)
  229. if s.client.Conf.EnableCRC {
  230. reader.writer = crc64.New(crc64.MakeTable(crc64.ECMA))
  231. }
  232. if opt != nil && opt.Listener != nil {
  233. reader.listener = opt.Listener
  234. }
  235. var res ImageProcessResult
  236. sendOpt := sendOptions{
  237. baseURL: s.client.BaseURL.BucketURL,
  238. uri: "/" + encodeURIComponent(name),
  239. method: http.MethodPut,
  240. body: reader,
  241. optHeader: opt,
  242. result: &res,
  243. }
  244. resp, err := s.client.send(ctx, &sendOpt)
  245. return &res, resp, err
  246. }
  247. // ci put object from local file
  248. func (s *CIService) PutFromFile(ctx context.Context, name string, filePath string, opt *ObjectPutOptions) (*ImageProcessResult, *Response, error) {
  249. fd, err := os.Open(filePath)
  250. if err != nil {
  251. return nil, nil, err
  252. }
  253. defer fd.Close()
  254. return s.Put(ctx, name, fd, opt)
  255. }
  256. // 基本图片处理 https://cloud.tencent.com/document/product/460/36540
  257. // 盲水印-下载时添加 https://cloud.tencent.com/document/product/460/19017
  258. func (s *CIService) Get(ctx context.Context, name string, operation string, opt *ObjectGetOptions, id ...string) (*Response, error) {
  259. var u string
  260. if len(id) == 1 {
  261. u = fmt.Sprintf("/%s?versionId=%s&%s", encodeURIComponent(name), id[0], operation)
  262. } else if len(id) == 0 {
  263. u = fmt.Sprintf("/%s?%s", encodeURIComponent(name), operation)
  264. } else {
  265. return nil, errors.New("wrong params")
  266. }
  267. sendOpt := sendOptions{
  268. baseURL: s.client.BaseURL.BucketURL,
  269. uri: u,
  270. method: http.MethodGet,
  271. optQuery: opt,
  272. optHeader: opt,
  273. disableCloseBody: true,
  274. }
  275. resp, err := s.client.send(ctx, &sendOpt)
  276. if opt != nil && opt.Listener != nil {
  277. if err == nil && resp != nil {
  278. if totalBytes, e := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64); e == nil {
  279. resp.Body = TeeReader(resp.Body, nil, totalBytes, opt.Listener)
  280. }
  281. }
  282. }
  283. return resp, err
  284. }
  285. func (s *CIService) GetToFile(ctx context.Context, name, localpath, operation string, opt *ObjectGetOptions, id ...string) (*Response, error) {
  286. resp, err := s.Get(ctx, name, operation, opt, id...)
  287. if err != nil {
  288. return resp, err
  289. }
  290. defer resp.Body.Close()
  291. // If file exist, overwrite it
  292. fd, err := os.OpenFile(localpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660)
  293. if err != nil {
  294. return resp, err
  295. }
  296. _, err = io.Copy(fd, resp.Body)
  297. fd.Close()
  298. if err != nil {
  299. return resp, err
  300. }
  301. return resp, nil
  302. }
  303. type GetQRcodeResult struct {
  304. XMLName xml.Name `xml:"Response"`
  305. CodeStatus int `xml:"CodeStatus,omitempty"`
  306. QRcodeInfo *QRcodeInfo `xml:"QRcodeInfo,omitempty"`
  307. ResultImage string `xml:"ResultImage,omitempty"`
  308. }
  309. // 二维码识别-下载时识别 https://cloud.tencent.com/document/product/436/54070
  310. func (s *CIService) GetQRcode(ctx context.Context, name string, cover int, opt *ObjectGetOptions, id ...string) (*GetQRcodeResult, *Response, error) {
  311. var u string
  312. if len(id) == 1 {
  313. u = fmt.Sprintf("/%s?versionId=%s&ci-process=QRcode&cover=%v", encodeURIComponent(name), id[0], cover)
  314. } else if len(id) == 0 {
  315. u = fmt.Sprintf("/%s?ci-process=QRcode&cover=%v", encodeURIComponent(name), cover)
  316. } else {
  317. return nil, nil, errors.New("wrong params")
  318. }
  319. var res GetQRcodeResult
  320. sendOpt := sendOptions{
  321. baseURL: s.client.BaseURL.BucketURL,
  322. uri: u,
  323. method: http.MethodGet,
  324. optQuery: opt,
  325. optHeader: opt,
  326. result: &res,
  327. }
  328. resp, err := s.client.send(ctx, &sendOpt)
  329. return &res, resp, err
  330. }
  331. type GenerateQRcodeOptions struct {
  332. QRcodeContent string `url:"qrcode-content,omitempty"`
  333. Mode int `url:"mode,omitempty"`
  334. Width int `url:"width,omitempty"`
  335. }
  336. type GenerateQRcodeResult struct {
  337. XMLName xml.Name `xml:"Response"`
  338. ResultImage string `xml:"ResultImage,omitempty"`
  339. }
  340. // 二维码生成 https://cloud.tencent.com/document/product/436/54071
  341. func (s *CIService) GenerateQRcode(ctx context.Context, opt *GenerateQRcodeOptions) (*GenerateQRcodeResult, *Response, error) {
  342. var res GenerateQRcodeResult
  343. sendOpt := &sendOptions{
  344. baseURL: s.client.BaseURL.BucketURL,
  345. uri: "/?ci-process=qrcode-generate",
  346. method: http.MethodGet,
  347. optQuery: opt,
  348. result: &res,
  349. }
  350. resp, err := s.client.send(ctx, sendOpt)
  351. return &res, resp, err
  352. }
  353. func (s *CIService) GenerateQRcodeToFile(ctx context.Context, filePath string, opt *GenerateQRcodeOptions) (*GenerateQRcodeResult, *Response, error) {
  354. res, resp, err := s.GenerateQRcode(ctx, opt)
  355. if err != nil {
  356. return res, resp, err
  357. }
  358. // If file exist, overwrite it
  359. fd, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660)
  360. if err != nil {
  361. return res, resp, err
  362. }
  363. defer fd.Close()
  364. bs, err := base64.StdEncoding.DecodeString(res.ResultImage)
  365. if err != nil {
  366. return res, resp, err
  367. }
  368. fb := bytes.NewReader(bs)
  369. _, err = io.Copy(fd, fb)
  370. return res, resp, err
  371. }