123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- package bfs
- import (
- "bytes"
- "context"
- "crypto/md5"
- "encoding/hex"
- "errors"
- "fmt"
- "io"
- "mime/multipart"
- "net/http"
- "net/url"
- "strconv"
- "strings"
- "time"
- "go-common/library/ecode"
- xhttp "go-common/library/net/http/blademaster"
- xtime "go-common/library/time"
- )
- const (
- _appKey = "appkey"
- _ts = "ts"
- )
- var (
- _defaultHost = "http://api.bilibili.co"
- _pathUpload = "/x/internal/upload"
- _pathGenWatermark = "/x/internal/image/gen"
- _defaultHTTPClientConfig = &xhttp.ClientConfig{
- App: &xhttp.App{
- Key: "3c4e41f926e51656",
- Secret: "26a2095b60c24154521d24ae62b885bb",
- },
- Dial: xtime.Duration(1 * time.Second),
- Timeout: xtime.Duration(1 * time.Second),
- }
- errBucket = errors.New("bucket is empty")
- errFile = errors.New("file is empty")
- )
- // Config bfs upload config
- type Config struct {
- Host string
- HTTPClient *xhttp.ClientConfig
- }
- // Request bfs upload request
- type Request struct {
- Bucket string
- Dir string
- ContentType string
- Filename string
- File []byte
- WMKey string
- WMText string
- WMPaddingX uint32
- WMPaddingY uint32
- WMScale float64
- }
- // verify verify bfs request.
- func (r *Request) verify() error {
- if r.Bucket == "" {
- return errBucket
- }
- if r.File == nil || len(r.File) == 0 {
- return errFile
- }
- return nil
- }
- // BFS bfs instance
- type BFS struct {
- conf *Config
- client *xhttp.Client
- }
- // New new a bfs client.
- func New(c *Config) *BFS {
- if c == nil {
- c = &Config{
- Host: _defaultHost,
- HTTPClient: _defaultHTTPClientConfig,
- }
- }
- return &BFS{
- conf: c,
- client: xhttp.NewClient(c.HTTPClient),
- }
- }
- // Upload bfs internal upload.
- func (b *BFS) Upload(ctx context.Context, req *Request) (location string, err error) {
- var (
- buf = &bytes.Buffer{}
- bw io.Writer
- request *http.Request
- response *struct {
- Code int `json:"code"`
- Data struct {
- ETag string `json:"etag"`
- Location string `json:"location"`
- } `json:"data"`
- Message string `json:"message"`
- }
- url = b.conf.Host + _pathUpload + "?" + b.sign()
- )
- if err = req.verify(); err != nil {
- return
- }
- w := multipart.NewWriter(buf)
- if bw, err = w.CreateFormFile("file", "bfs-upload"); err != nil {
- return
- }
- if _, err = bw.Write(req.File); err != nil {
- return
- }
- w.WriteField("bucket", req.Bucket)
- if req.Filename != "" {
- w.WriteField("file_name", req.Filename)
- }
- if req.Dir != "" {
- w.WriteField("dir", req.Dir)
- }
- if req.WMText != "" {
- w.WriteField("wm_text", req.WMKey)
- }
- if req.WMKey != "" {
- w.WriteField("wm_key", req.WMKey)
- }
- if req.WMPaddingX > 0 {
- w.WriteField("wm_padding_x", fmt.Sprint(req.WMPaddingX))
- }
- if req.WMPaddingY > 0 {
- w.WriteField("wm_padding_y", fmt.Sprint(req.WMPaddingY))
- }
- if req.WMScale > 0 {
- w.WriteField("wm_scale", strconv.FormatFloat(req.WMScale, 'f', 2, 64))
- }
- if req.ContentType != "" {
- w.WriteField("content_type", req.ContentType)
- }
- if err = w.Close(); err != nil {
- return
- }
- if request, err = http.NewRequest(http.MethodPost, url, buf); err != nil {
- return
- }
- request.Header.Set("Content-Type", w.FormDataContentType())
- if err = b.client.Do(ctx, request, &response); err != nil {
- return
- }
- if !ecode.Int(response.Code).Equal(ecode.OK) {
- err = ecode.Int(response.Code)
- return
- }
- location = response.Data.Location
- return
- }
- // GenWatermark create watermark image by key and text.
- func (b *BFS) GenWatermark(ctx context.Context, uploadKey, wmKey, wmText string, vertical bool, distance int) (location string, err error) {
- var (
- params = url.Values{}
- uri = b.conf.Host + _pathGenWatermark
- response *struct {
- Code int `json:"code"`
- Data struct {
- Location string `json:"location"`
- Width int `json:"width"`
- Height int `json:"height"`
- MD5 string `json:"md5"`
- } `json:"data"`
- Message string `json:"message"`
- }
- )
- params.Set("upload_key", uploadKey)
- params.Set("wm_key", wmKey)
- params.Set("wm_text", wmText)
- params.Set("wm_vertical", fmt.Sprint(vertical))
- params.Set("distance", fmt.Sprint(distance))
- if err = b.client.Post(ctx, uri, "", params, &response); err != nil {
- return
- }
- if !ecode.Int(response.Code).Equal(ecode.OK) {
- err = ecode.Int(response.Code)
- return
- }
- location = response.Data.Location
- return
- }
- // sign calc appkey and appsecret sign.
- func (b *BFS) sign() string {
- key := b.conf.HTTPClient.Key
- secret := b.conf.HTTPClient.Secret
- params := url.Values{}
- params.Set(_appKey, key)
- params.Set(_ts, strconv.FormatInt(time.Now().Unix(), 10))
- tmp := params.Encode()
- if strings.IndexByte(tmp, '+') > -1 {
- tmp = strings.Replace(tmp, "+", "%20", -1)
- }
- var buf bytes.Buffer
- buf.WriteString(tmp)
- buf.WriteString(secret)
- mh := md5.Sum(buf.Bytes())
- // query
- var qb bytes.Buffer
- qb.WriteString(tmp)
- qb.WriteString("&sign=")
- qb.WriteString(hex.EncodeToString(mh[:]))
- return qb.String()
- }
|