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.

491 lines
16 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
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 *AuditingJobDetail `xml:",omitempty"`
  172. NonExistJobIds string `xml:",omitempty"`
  173. }
  174. type AuditingJobDetail 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. type PutAudioAuditingJobOptions struct {
  209. XMLName xml.Name `xml:"Request"`
  210. InputObject string `xml:"Input>Object"`
  211. Conf *AudioAuditingJobConf `xml:"Conf"`
  212. }
  213. type AudioAuditingJobConf struct {
  214. DetectType string `xml:",omitempty"`
  215. Callback string `xml:",omitempty"`
  216. }
  217. type PutAudioAuditingJobResult PutVideoAuditingJobResult
  218. type GetAudioAuditingJobResult GetVideoAuditingJobResult
  219. // 音频审核-创建任务 https://cloud.tencent.com/document/product/460/53395
  220. func (s *CIService) PutAudioAuditingJob(ctx context.Context, opt *PutAudioAuditingJobOptions) (*PutAudioAuditingJobResult, *Response, error) {
  221. var res PutAudioAuditingJobResult
  222. sendOpt := sendOptions{
  223. baseURL: s.client.BaseURL.CIURL,
  224. uri: "/audio/auditing",
  225. method: http.MethodPost,
  226. body: opt,
  227. result: &res,
  228. }
  229. resp, err := s.client.send(ctx, &sendOpt)
  230. return &res, resp, err
  231. }
  232. // 音频审核-查询任务 https://cloud.tencent.com/document/product/460/53396
  233. func (s *CIService) GetAudioAuditingJob(ctx context.Context, jobid string) (*GetAudioAuditingJobResult, *Response, error) {
  234. var res GetAudioAuditingJobResult
  235. sendOpt := sendOptions{
  236. baseURL: s.client.BaseURL.CIURL,
  237. uri: "/audio/auditing/" + jobid,
  238. method: http.MethodGet,
  239. result: &res,
  240. }
  241. resp, err := s.client.send(ctx, &sendOpt)
  242. return &res, resp, err
  243. }
  244. // 图片持久化处理-上传时处理 https://cloud.tencent.com/document/product/460/18147
  245. // 盲水印-上传时添加 https://cloud.tencent.com/document/product/460/19017
  246. // 二维码识别-上传时识别 https://cloud.tencent.com/document/product/460/37513
  247. func (s *CIService) Put(ctx context.Context, name string, r io.Reader, uopt *ObjectPutOptions) (*ImageProcessResult, *Response, error) {
  248. if r == nil {
  249. return nil, nil, fmt.Errorf("reader is nil")
  250. }
  251. if err := CheckReaderLen(r); err != nil {
  252. return nil, nil, err
  253. }
  254. opt := CloneObjectPutOptions(uopt)
  255. totalBytes, err := GetReaderLen(r)
  256. if err != nil && opt != nil && opt.Listener != nil {
  257. if opt.ContentLength == 0 {
  258. return nil, nil, err
  259. }
  260. totalBytes = opt.ContentLength
  261. }
  262. if err == nil {
  263. // 与 go http 保持一致, 非bytes.Buffer/bytes.Reader/strings.Reader由用户指定ContentLength, 或使用 Chunk 上传
  264. if opt != nil && opt.ContentLength == 0 && IsLenReader(r) {
  265. opt.ContentLength = totalBytes
  266. }
  267. }
  268. reader := TeeReader(r, nil, totalBytes, nil)
  269. if s.client.Conf.EnableCRC {
  270. reader.writer = crc64.New(crc64.MakeTable(crc64.ECMA))
  271. }
  272. if opt != nil && opt.Listener != nil {
  273. reader.listener = opt.Listener
  274. }
  275. var res ImageProcessResult
  276. sendOpt := sendOptions{
  277. baseURL: s.client.BaseURL.BucketURL,
  278. uri: "/" + encodeURIComponent(name),
  279. method: http.MethodPut,
  280. body: reader,
  281. optHeader: opt,
  282. result: &res,
  283. }
  284. resp, err := s.client.send(ctx, &sendOpt)
  285. return &res, resp, err
  286. }
  287. // ci put object from local file
  288. func (s *CIService) PutFromFile(ctx context.Context, name string, filePath string, opt *ObjectPutOptions) (*ImageProcessResult, *Response, error) {
  289. fd, err := os.Open(filePath)
  290. if err != nil {
  291. return nil, nil, err
  292. }
  293. defer fd.Close()
  294. return s.Put(ctx, name, fd, opt)
  295. }
  296. // 基本图片处理 https://cloud.tencent.com/document/product/460/36540
  297. // 盲水印-下载时添加 https://cloud.tencent.com/document/product/460/19017
  298. func (s *CIService) Get(ctx context.Context, name string, operation string, opt *ObjectGetOptions, id ...string) (*Response, error) {
  299. var u string
  300. if len(id) == 1 {
  301. u = fmt.Sprintf("/%s?versionId=%s&%s", encodeURIComponent(name), id[0], operation)
  302. } else if len(id) == 0 {
  303. u = fmt.Sprintf("/%s?%s", encodeURIComponent(name), operation)
  304. } else {
  305. return nil, errors.New("wrong params")
  306. }
  307. sendOpt := sendOptions{
  308. baseURL: s.client.BaseURL.BucketURL,
  309. uri: u,
  310. method: http.MethodGet,
  311. optQuery: opt,
  312. optHeader: opt,
  313. disableCloseBody: true,
  314. }
  315. resp, err := s.client.send(ctx, &sendOpt)
  316. if opt != nil && opt.Listener != nil {
  317. if err == nil && resp != nil {
  318. if totalBytes, e := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64); e == nil {
  319. resp.Body = TeeReader(resp.Body, nil, totalBytes, opt.Listener)
  320. }
  321. }
  322. }
  323. return resp, err
  324. }
  325. func (s *CIService) GetToFile(ctx context.Context, name, localpath, operation string, opt *ObjectGetOptions, id ...string) (*Response, error) {
  326. resp, err := s.Get(ctx, name, operation, opt, id...)
  327. if err != nil {
  328. return resp, err
  329. }
  330. defer resp.Body.Close()
  331. // If file exist, overwrite it
  332. fd, err := os.OpenFile(localpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660)
  333. if err != nil {
  334. return resp, err
  335. }
  336. _, err = io.Copy(fd, resp.Body)
  337. fd.Close()
  338. if err != nil {
  339. return resp, err
  340. }
  341. return resp, nil
  342. }
  343. type GetQRcodeResult struct {
  344. XMLName xml.Name `xml:"Response"`
  345. CodeStatus int `xml:"CodeStatus,omitempty"`
  346. QRcodeInfo *QRcodeInfo `xml:"QRcodeInfo,omitempty"`
  347. ResultImage string `xml:"ResultImage,omitempty"`
  348. }
  349. // 二维码识别-下载时识别 https://cloud.tencent.com/document/product/436/54070
  350. func (s *CIService) GetQRcode(ctx context.Context, name string, cover int, opt *ObjectGetOptions, id ...string) (*GetQRcodeResult, *Response, error) {
  351. var u string
  352. if len(id) == 1 {
  353. u = fmt.Sprintf("/%s?versionId=%s&ci-process=QRcode&cover=%v", encodeURIComponent(name), id[0], cover)
  354. } else if len(id) == 0 {
  355. u = fmt.Sprintf("/%s?ci-process=QRcode&cover=%v", encodeURIComponent(name), cover)
  356. } else {
  357. return nil, nil, errors.New("wrong params")
  358. }
  359. var res GetQRcodeResult
  360. sendOpt := sendOptions{
  361. baseURL: s.client.BaseURL.BucketURL,
  362. uri: u,
  363. method: http.MethodGet,
  364. optQuery: opt,
  365. optHeader: opt,
  366. result: &res,
  367. }
  368. resp, err := s.client.send(ctx, &sendOpt)
  369. return &res, resp, err
  370. }
  371. type GenerateQRcodeOptions struct {
  372. QRcodeContent string `url:"qrcode-content,omitempty"`
  373. Mode int `url:"mode,omitempty"`
  374. Width int `url:"width,omitempty"`
  375. }
  376. type GenerateQRcodeResult struct {
  377. XMLName xml.Name `xml:"Response"`
  378. ResultImage string `xml:"ResultImage,omitempty"`
  379. }
  380. // 二维码生成 https://cloud.tencent.com/document/product/436/54071
  381. func (s *CIService) GenerateQRcode(ctx context.Context, opt *GenerateQRcodeOptions) (*GenerateQRcodeResult, *Response, error) {
  382. var res GenerateQRcodeResult
  383. sendOpt := &sendOptions{
  384. baseURL: s.client.BaseURL.BucketURL,
  385. uri: "/?ci-process=qrcode-generate",
  386. method: http.MethodGet,
  387. optQuery: opt,
  388. result: &res,
  389. }
  390. resp, err := s.client.send(ctx, sendOpt)
  391. return &res, resp, err
  392. }
  393. func (s *CIService) GenerateQRcodeToFile(ctx context.Context, filePath string, opt *GenerateQRcodeOptions) (*GenerateQRcodeResult, *Response, error) {
  394. res, resp, err := s.GenerateQRcode(ctx, opt)
  395. if err != nil {
  396. return res, resp, err
  397. }
  398. // If file exist, overwrite it
  399. fd, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660)
  400. if err != nil {
  401. return res, resp, err
  402. }
  403. defer fd.Close()
  404. bs, err := base64.StdEncoding.DecodeString(res.ResultImage)
  405. if err != nil {
  406. return res, resp, err
  407. }
  408. fb := bytes.NewReader(bs)
  409. _, err = io.Copy(fd, fb)
  410. return res, resp, err
  411. }
  412. // 开通 Guetzli 压缩 https://cloud.tencent.com/document/product/460/30112
  413. func (s *CIService) PutGuetzli(ctx context.Context) (*Response, error) {
  414. sendOpt := &sendOptions{
  415. baseURL: s.client.BaseURL.CIURL,
  416. uri: "/?guetzli",
  417. method: http.MethodPut,
  418. }
  419. resp, err := s.client.send(ctx, sendOpt)
  420. return resp, err
  421. }
  422. type GetGuetzliResult struct {
  423. XMLName xml.Name `xml:"GuetzliStatus"`
  424. GuetzliStatus string `xml:",chardata"`
  425. }
  426. // 查询 Guetzli 状态 https://cloud.tencent.com/document/product/460/30111
  427. func (s *CIService) GetGuetzli(ctx context.Context) (*GetGuetzliResult, *Response, error) {
  428. var res GetGuetzliResult
  429. sendOpt := &sendOptions{
  430. baseURL: s.client.BaseURL.CIURL,
  431. uri: "/?guetzli",
  432. method: http.MethodGet,
  433. result: &res,
  434. }
  435. resp, err := s.client.send(ctx, sendOpt)
  436. return &res, resp, err
  437. }
  438. // 关闭 Guetzli 压缩 https://cloud.tencent.com/document/product/460/30113
  439. func (s *CIService) DeleteGuetzli(ctx context.Context) (*Response, error) {
  440. sendOpt := &sendOptions{
  441. baseURL: s.client.BaseURL.CIURL,
  442. uri: "/?guetzli",
  443. method: http.MethodDelete,
  444. }
  445. resp, err := s.client.send(ctx, sendOpt)
  446. return resp, err
  447. }