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.

488 lines
16 KiB

package cos
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"hash/crc64"
"io"
"net/http"
"os"
"strconv"
)
type CIService service
type PicOperations struct {
IsPicInfo int `json:"is_pic_info,omitempty"`
Rules []PicOperationsRules `json:"rules,omitemtpy"`
}
type PicOperationsRules struct {
Bucket string `json:"bucket,omitempty"`
FileId string `json:"fileid"`
Rule string `json:"rule"`
}
func EncodePicOperations(pic *PicOperations) string {
if pic == nil {
return ""
}
bs, err := json.Marshal(pic)
if err != nil {
return ""
}
return string(bs)
}
type ImageProcessResult struct {
XMLName xml.Name `xml:"UploadResult"`
OriginalInfo *PicOriginalInfo `xml:"OriginalInfo,omitempty"`
ProcessResults *PicProcessObject `xml:"ProcessResults>Object,omitempty"`
}
type PicOriginalInfo struct {
Key string `xml:"Key,omitempty"`
Location string `xml:"Location,omitempty"`
ImageInfo *PicImageInfo `xml:"ImageInfo,omitempty"`
ETag string `xml:"ETag,omitempty"`
}
type PicImageInfo struct {
Format string `xml:"Format,omitempty"`
Width int `xml:"Width,omitempty"`
Height int `xml:"Height,omitempty"`
Quality int `xml:"Quality,omitempty"`
Ave string `xml:"Ave,omitempty"`
Orientation int `xml:"Orientation,omitempty"`
}
type PicProcessObject struct {
Key string `xml:"Key,omitempty"`
Location string `xml:"Location,omitempty"`
Format string `xml:"Format,omitempty"`
Width int `xml:"Width,omitempty"`
Height int `xml:"Height,omitempty"`
Size int `xml:"Size,omitempty"`
Quality int `xml:"Quality,omitempty"`
ETag string `xml:"ETag,omitempty"`
WatermarkStatus int `xml:"WatermarkStatus,omitempty"`
CodeStatus int `xml:"CodeStatus,omitempty"`
QRcodeInfo []QRcodeInfo `xml:"QRcodeInfo,omitempty"`
}
type QRcodeInfo struct {
CodeUrl string `xml:"CodeUrl,omitempty"`
CodeLocation *CodeLocation `xml:"CodeLocation,omitempty"`
}
type CodeLocation struct {
Point []string `xml:"Point,omitempty"`
}
type picOperationsHeader struct {
PicOperations string `header:"Pic-Operations" xml:"-" url:"-"`
}
type ImageProcessOptions = PicOperations
// 云上数据处理 https://cloud.tencent.com/document/product/460/18147
func (s *CIService) ImageProcess(ctx context.Context, name string, opt *ImageProcessOptions) (*ImageProcessResult, *Response, error) {
header := &picOperationsHeader{
PicOperations: EncodePicOperations(opt),
}
var res ImageProcessResult
sendOpt := sendOptions{
baseURL: s.client.BaseURL.BucketURL,
uri: "/" + encodeURIComponent(name) + "?image_process",
method: http.MethodPost,
optHeader: header,
result: &res,
}
resp, err := s.client.send(ctx, &sendOpt)
return &res, resp, err
}
type ImageRecognitionOptions struct {
CIProcess string `url:"ci-process,omitempty"`
DetectType string `url:"detect-type,omitempty"`
}
type ImageRecognitionResult struct {
XMLName xml.Name `xml:"RecognitionResult"`
PornInfo *RecognitionInfo `xml:"PornInfo,omitempty"`
TerroristInfo *RecognitionInfo `xml:"TerroristInfo,omitempty"`
PoliticsInfo *RecognitionInfo `xml:"PoliticsInfo,omitempty"`
AdsInfo *RecognitionInfo `xml:"AdsInfo,omitempty"`
}
type RecognitionInfo struct {
Code int `xml:"Code,omitempty"`
Msg string `xml:"Msg,omitempty"`
HitFlag int `xml:"HitFlag,omitempty"`
Score int `xml:"Score,omitempty"`
Label string `xml:"Label,omitempty"`
Count int `xml:"Count,omitempty"`
}
// 图片审核 https://cloud.tencent.com/document/product/460/37318
func (s *CIService) ImageRecognition(ctx context.Context, name string, DetectType string) (*ImageRecognitionResult, *Response, error) {
opt := &ImageRecognitionOptions{
CIProcess: "sensitive-content-recognition",
DetectType: DetectType,
}
var res ImageRecognitionResult
sendOpt := sendOptions{
baseURL: s.client.BaseURL.BucketURL,
uri: "/" + encodeURIComponent(name),
method: http.MethodGet,
optQuery: opt,
result: &res,
}
resp, err := s.client.send(ctx, &sendOpt)
return &res, resp, err
}
type PutVideoAuditingJobOptions struct {
XMLName xml.Name `xml:"Request"`
InputObject string `xml:"Input>Object"`
Conf *VideoAuditingJobConf `xml:"Conf"`
}
type VideoAuditingJobConf struct {
DetectType string `xml:",omitempty"`
Snapshot *PutVideoAuditingJobSnapshot `xml:",omitempty"`
Callback string `xml:",omitempty"`
}
type PutVideoAuditingJobSnapshot struct {
Mode string `xml:",omitempty"`
Count int `xml:",omitempty"`
TimeInterval float32 `xml:",omitempty"`
Start float32 `xml:",omitempty"`
}
type PutVideoAuditingJobResult struct {
XMLName xml.Name `xml:"Response"`
JobsDetail struct {
JobId string `xml:"JobId,omitempty"`
State string `xml:"State,omitempty"`
CreationTime string `xml:"CreationTime,omitempty"`
Object string `xml:"Object,omitempty"`
} `xml:"JobsDetail,omitempty"`
}
// 视频审核-创建任务 https://cloud.tencent.com/document/product/460/46427
func (s *CIService) PutVideoAuditingJob(ctx context.Context, opt *PutVideoAuditingJobOptions) (*PutVideoAuditingJobResult, *Response, error) {
var res PutVideoAuditingJobResult
sendOpt := sendOptions{
baseURL: s.client.BaseURL.CIURL,
uri: "/video/auditing",
method: http.MethodPost,
body: opt,
result: &res,
}
resp, err := s.client.send(ctx, &sendOpt)
return &res, resp, err
}
type GetVideoAuditingJobResult struct {
XMLName xml.Name `xml:"Response"`
JobsDetail *AuditingJobDetail `xml:",omitempty"`
NonExistJobIds string `xml:",omitempty"`
}
type AuditingJobDetail struct {
Code string `xml:",omitempty"`
Message string `xml:",omitempty"`
JobId string `xml:",omitempty"`
State string `xml:",omitempty"`
CreationTime string `xml:",omitempty"`
Object string `xml:",omitempty"`
SnapshotCount string `xml:",omitempty"`
Result int `xml:",omitempty"`
PornInfo *RecognitionInfo `xml:",omitempty"`
TerrorismInfo *RecognitionInfo `xml:",omitempty"`
PoliticsInfo *RecognitionInfo `xml:",omitempty"`
AdsInfo *RecognitionInfo `xml:",omitempty"`
Snapshot *GetVideoAuditingJobSnapshot `xml:",omitempty"`
}
type GetVideoAuditingJobSnapshot struct {
Url string `xml:",omitempty"`
PornInfo *RecognitionInfo `xml:",omitempty"`
TerrorismInfo *RecognitionInfo `xml:",omitempty"`
PoliticsInfo *RecognitionInfo `xml:",omitempty"`
AdsInfo *RecognitionInfo `xml:",omitempty"`
}
// 视频审核-查询任务 https://cloud.tencent.com/document/product/460/46926
func (s *CIService) GetVideoAuditingJob(ctx context.Context, jobid string) (*GetVideoAuditingJobResult, *Response, error) {
var res GetVideoAuditingJobResult
sendOpt := sendOptions{
baseURL: s.client.BaseURL.CIURL,
uri: "/video/auditing/" + jobid,
method: http.MethodGet,
result: &res,
}
resp, err := s.client.send(ctx, &sendOpt)
return &res, resp, err
}
type PutAudioAuditingJobOptions struct {
XMLName xml.Name `xml:"Request"`
InputObject string `xml:"Input>Object"`
Conf *AudioAuditingJobConf `xml:"Conf"`
}
type AudioAuditingJobConf struct {
DetectType string `xml:",omitempty"`
Callback string `xml:",omitempty"`
}
type PutAudioAuditingJobResult PutVideoAuditingJobResult
type GetAudioAuditingJobResult GetVideoAuditingJobResult
// 音频审核-创建任务 https://cloud.tencent.com/document/product/460/53395
func (s *CIService) PutAudioAuditingJob(ctx context.Context, opt *PutAudioAuditingJobOptions) (*PutAudioAuditingJobResult, *Response, error) {
var res PutAudioAuditingJobResult
sendOpt := sendOptions{
baseURL: s.client.BaseURL.CIURL,
uri: "/audio/auditing",
method: http.MethodPost,
body: opt,
result: &res,
}
resp, err := s.client.send(ctx, &sendOpt)
return &res, resp, err
}
// 音频审核-查询任务 https://cloud.tencent.com/document/product/460/53396
func (s *CIService) GetAudioAuditingJob(ctx context.Context, jobid string) (*GetAudioAuditingJobResult, *Response, error) {
var res GetAudioAuditingJobResult
sendOpt := sendOptions{
baseURL: s.client.BaseURL.CIURL,
uri: "/audio/auditing/" + jobid,
method: http.MethodGet,
result: &res,
}
resp, err := s.client.send(ctx, &sendOpt)
return &res, resp, err
}
// 图片持久化处理-上传时处理 https://cloud.tencent.com/document/product/460/18147
// 盲水印-上传时添加 https://cloud.tencent.com/document/product/460/19017
// 二维码识别-上传时识别 https://cloud.tencent.com/document/product/460/37513
func (s *CIService) Put(ctx context.Context, name string, r io.Reader, uopt *ObjectPutOptions) (*ImageProcessResult, *Response, error) {
if r == nil {
return nil, nil, fmt.Errorf("reader is nil")
}
if err := CheckReaderLen(r); err != nil {
return nil, nil, err
}
opt := cloneObjectPutOptions(uopt)
totalBytes, err := GetReaderLen(r)
if err != nil && opt != nil && opt.Listener != nil {
return nil, nil, err
}
if err == nil {
// 与 go http 保持一致, 非bytes.Buffer/bytes.Reader/strings.Reader由用户指定ContentLength, 或使用 Chunk 上传
if opt != nil && opt.ContentLength == 0 && IsLenReader(r) {
opt.ContentLength = totalBytes
}
}
reader := TeeReader(r, nil, totalBytes, nil)
if s.client.Conf.EnableCRC {
reader.writer = crc64.New(crc64.MakeTable(crc64.ECMA))
}
if opt != nil && opt.Listener != nil {
reader.listener = opt.Listener
}
var res ImageProcessResult
sendOpt := sendOptions{
baseURL: s.client.BaseURL.BucketURL,
uri: "/" + encodeURIComponent(name),
method: http.MethodPut,
body: reader,
optHeader: opt,
result: &res,
}
resp, err := s.client.send(ctx, &sendOpt)
return &res, resp, err
}
// ci put object from local file
func (s *CIService) PutFromFile(ctx context.Context, name string, filePath string, opt *ObjectPutOptions) (*ImageProcessResult, *Response, error) {
fd, err := os.Open(filePath)
if err != nil {
return nil, nil, err
}
defer fd.Close()
return s.Put(ctx, name, fd, opt)
}
// 基本图片处理 https://cloud.tencent.com/document/product/460/36540
// 盲水印-下载时添加 https://cloud.tencent.com/document/product/460/19017
func (s *CIService) Get(ctx context.Context, name string, operation string, opt *ObjectGetOptions, id ...string) (*Response, error) {
var u string
if len(id) == 1 {
u = fmt.Sprintf("/%s?versionId=%s&%s", encodeURIComponent(name), id[0], operation)
} else if len(id) == 0 {
u = fmt.Sprintf("/%s?%s", encodeURIComponent(name), operation)
} else {
return nil, errors.New("wrong params")
}
sendOpt := sendOptions{
baseURL: s.client.BaseURL.BucketURL,
uri: u,
method: http.MethodGet,
optQuery: opt,
optHeader: opt,
disableCloseBody: true,
}
resp, err := s.client.send(ctx, &sendOpt)
if opt != nil && opt.Listener != nil {
if err == nil && resp != nil {
if totalBytes, e := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64); e == nil {
resp.Body = TeeReader(resp.Body, nil, totalBytes, opt.Listener)
}
}
}
return resp, err
}
func (s *CIService) GetToFile(ctx context.Context, name, localpath, operation string, opt *ObjectGetOptions, id ...string) (*Response, error) {
resp, err := s.Get(ctx, name, operation, opt, id...)
if err != nil {
return resp, err
}
defer resp.Body.Close()
// If file exist, overwrite it
fd, err := os.OpenFile(localpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660)
if err != nil {
return resp, err
}
_, err = io.Copy(fd, resp.Body)
fd.Close()
if err != nil {
return resp, err
}
return resp, nil
}
type GetQRcodeResult struct {
XMLName xml.Name `xml:"Response"`
CodeStatus int `xml:"CodeStatus,omitempty"`
QRcodeInfo *QRcodeInfo `xml:"QRcodeInfo,omitempty"`
ResultImage string `xml:"ResultImage,omitempty"`
}
// 二维码识别-下载时识别 https://cloud.tencent.com/document/product/436/54070
func (s *CIService) GetQRcode(ctx context.Context, name string, cover int, opt *ObjectGetOptions, id ...string) (*GetQRcodeResult, *Response, error) {
var u string
if len(id) == 1 {
u = fmt.Sprintf("/%s?versionId=%s&ci-process=QRcode&cover=%v", encodeURIComponent(name), id[0], cover)
} else if len(id) == 0 {
u = fmt.Sprintf("/%s?ci-process=QRcode&cover=%v", encodeURIComponent(name), cover)
} else {
return nil, nil, errors.New("wrong params")
}
var res GetQRcodeResult
sendOpt := sendOptions{
baseURL: s.client.BaseURL.BucketURL,
uri: u,
method: http.MethodGet,
optQuery: opt,
optHeader: opt,
result: &res,
}
resp, err := s.client.send(ctx, &sendOpt)
return &res, resp, err
}
type GenerateQRcodeOptions struct {
QRcodeContent string `url:"qrcode-content,omitempty"`
Mode int `url:"mode,omitempty"`
Width int `url:"width,omitempty"`
}
type GenerateQRcodeResult struct {
XMLName xml.Name `xml:"Response"`
ResultImage string `xml:"ResultImage,omitempty"`
}
// 二维码生成 https://cloud.tencent.com/document/product/436/54071
func (s *CIService) GenerateQRcode(ctx context.Context, opt *GenerateQRcodeOptions) (*GenerateQRcodeResult, *Response, error) {
var res GenerateQRcodeResult
sendOpt := &sendOptions{
baseURL: s.client.BaseURL.BucketURL,
uri: "/?ci-process=qrcode-generate",
method: http.MethodGet,
optQuery: opt,
result: &res,
}
resp, err := s.client.send(ctx, sendOpt)
return &res, resp, err
}
func (s *CIService) GenerateQRcodeToFile(ctx context.Context, filePath string, opt *GenerateQRcodeOptions) (*GenerateQRcodeResult, *Response, error) {
res, resp, err := s.GenerateQRcode(ctx, opt)
if err != nil {
return res, resp, err
}
// If file exist, overwrite it
fd, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660)
if err != nil {
return res, resp, err
}
defer fd.Close()
bs, err := base64.StdEncoding.DecodeString(res.ResultImage)
if err != nil {
return res, resp, err
}
fb := bytes.NewReader(bs)
_, err = io.Copy(fd, fb)
return res, resp, err
}
// 开通 Guetzli 压缩 https://cloud.tencent.com/document/product/460/30112
func (s *CIService) PutGuetzli(ctx context.Context) (*Response, error) {
sendOpt := &sendOptions{
baseURL: s.client.BaseURL.CIURL,
uri: "/?guetzli",
method: http.MethodPut,
}
resp, err := s.client.send(ctx, sendOpt)
return resp, err
}
type GetGuetzliResult struct {
XMLName xml.Name `xml:"GuetzliStatus"`
GuetzliStatus string `xml:",chardata"`
}
// 查询 Guetzli 状态 https://cloud.tencent.com/document/product/460/30111
func (s *CIService) GetGuetzli(ctx context.Context) (*GetGuetzliResult, *Response, error) {
var res GetGuetzliResult
sendOpt := &sendOptions{
baseURL: s.client.BaseURL.CIURL,
uri: "/?guetzli",
method: http.MethodGet,
result: &res,
}
resp, err := s.client.send(ctx, sendOpt)
return &res, resp, err
}
// 关闭 Guetzli 压缩 https://cloud.tencent.com/document/product/460/30113
func (s *CIService) DeleteGuetzli(ctx context.Context) (*Response, error) {
sendOpt := &sendOptions{
baseURL: s.client.BaseURL.CIURL,
uri: "/?guetzli",
method: http.MethodDelete,
}
resp, err := s.client.send(ctx, sendOpt)
return resp, err
}