283 lines
8.0 KiB
Go
283 lines
8.0 KiB
Go
|
package service
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"context"
|
||
|
"crypto/sha1"
|
||
|
"encoding/hex"
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"net/url"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"go-common/app/interface/main/upload/model"
|
||
|
"go-common/library/ecode"
|
||
|
"go-common/library/log"
|
||
|
"go-common/library/queue/databus/report"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
_genImageContentType = "image/png"
|
||
|
)
|
||
|
|
||
|
// GenImageUpload generate watermark image by text and upload it.
|
||
|
func (s *Service) GenImageUpload(ctx context.Context, uploadKey string, wmKey, wmText string, distance int, vertical bool) (res *model.ResultWm, err error) {
|
||
|
var image []byte
|
||
|
res = new(model.ResultWm)
|
||
|
key, secret, bucket := s.authorizeInfo(uploadKey)
|
||
|
if image, res.Height, res.Width, res.Md5, err = s.bfs.GenImage(ctx, wmKey, wmText, distance, vertical); err != nil {
|
||
|
return
|
||
|
}
|
||
|
res.Location, _, err = s.bfs.Upload(ctx, key, secret, _genImageContentType, bucket, "", "", image)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Upload upload by key and secret.
|
||
|
func (s *Service) Upload(ctx context.Context, uploadKey, uploadToken, contentType string, data []byte) (result *model.Result, err error) {
|
||
|
if !s.verify(uploadKey, uploadToken) {
|
||
|
err = ecode.AccessDenied
|
||
|
return
|
||
|
}
|
||
|
key, secret, bucket := s.authorizeInfo(uploadKey)
|
||
|
if contentType == "" {
|
||
|
contentType = http.DetectContentType(data)
|
||
|
}
|
||
|
location, etag, err := s.bfs.Upload(ctx, key, secret, contentType, bucket, "", "", data)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
result = &model.Result{
|
||
|
Location: location,
|
||
|
Etag: etag,
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// authorizeInfo get authorize info by upload key.
|
||
|
func (s *Service) authorizeInfo(uploadKey string) (key, secret, bucket string) {
|
||
|
key = s.c.BfsBucket.Key
|
||
|
secret = s.c.BfsBucket.Sercet
|
||
|
bucket = s.c.BfsBucket.Bucket
|
||
|
for _, a := range s.c.Auths {
|
||
|
if a.AppKey == uploadKey && a.BfsBucket != nil {
|
||
|
key = a.BfsBucket.Key
|
||
|
secret = a.BfsBucket.Sercet
|
||
|
bucket = a.BfsBucket.Bucket
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (s *Service) verify(key, token string) bool {
|
||
|
var (
|
||
|
expire, now, delta int64
|
||
|
err error
|
||
|
)
|
||
|
for _, auth := range s.c.Auths {
|
||
|
if key == auth.AppKey {
|
||
|
srcs := strings.Split(token, ":")
|
||
|
if len(srcs) != 2 {
|
||
|
log.Error("verify error: len(srcs) != 2")
|
||
|
return false
|
||
|
}
|
||
|
if expire, err = strconv.ParseInt(srcs[1], 10, 64); err != nil {
|
||
|
log.Error("verify error: expire not int (%v)", err)
|
||
|
return false
|
||
|
}
|
||
|
if s.gen(auth.AppKey, auth.AppSercet, expire) != srcs[0] {
|
||
|
log.Error("verify error: s.gen(%s,%s,%d) != srcs[0]:%s", auth.AppKey, auth.AppSercet, expire, srcs[0])
|
||
|
return false
|
||
|
}
|
||
|
now = time.Now().Unix()
|
||
|
// > ±15 min is forbidden
|
||
|
if expire > now {
|
||
|
delta = expire - now
|
||
|
} else {
|
||
|
delta = now - expire
|
||
|
}
|
||
|
if delta > 900 {
|
||
|
log.Error("verify error: delta > 900")
|
||
|
return false
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (s *Service) gen(key, sercet string, now int64) string {
|
||
|
sha1 := sha1.New()
|
||
|
sha1.Write([]byte(fmt.Sprintf("i love bilibili %s:%d", sercet, now)))
|
||
|
return hex.EncodeToString(sha1.Sum([]byte("")))
|
||
|
}
|
||
|
|
||
|
// UploadRecord .
|
||
|
func (s *Service) UploadRecord(ctx context.Context, action model.UploadActionType, mid int64, up *model.UploadParam, data []byte) (result *model.Result, err error) {
|
||
|
var (
|
||
|
location string
|
||
|
etag string
|
||
|
b *model.Bucket
|
||
|
ok bool
|
||
|
)
|
||
|
if b, ok = s.bucketCache[up.Bucket]; !ok {
|
||
|
err = ecode.BfsUplaodBucketNotExist
|
||
|
log.Error("read bucket items failed: (%s)", up.Bucket)
|
||
|
return
|
||
|
}
|
||
|
// content-type
|
||
|
if up.ContentType == "" {
|
||
|
up.ContentType = http.DetectContentType(data)
|
||
|
}
|
||
|
// check limit if dir not null
|
||
|
if up.Dir != "" {
|
||
|
up.Dir = strings.Trim(up.Dir, "/")
|
||
|
//todo: dir limit if conf exist
|
||
|
if err = s.dirlimit(up.Bucket, up.Dir, up.ContentType, data); err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
log.Info("upload record params:%+v", up)
|
||
|
if up.WmKey != "" || up.WmText != "" {
|
||
|
var image []byte
|
||
|
if image, err = s.bfs.Watermark(ctx, data, up.ContentType, up.WmKey, up.WmText, up.WmPaddingX, up.WmPaddingY, up.WmScale); err != nil {
|
||
|
log.Error("Upload.Watermark data length(%d) params(%+v) error(%v)", len(data), up, err)
|
||
|
} else {
|
||
|
data = image
|
||
|
}
|
||
|
}
|
||
|
if location, etag, err = s.bfs.Upload(ctx, b.KeyID, b.KeySecret, up.ContentType, up.Bucket, up.Dir, up.FileName, data); err != nil {
|
||
|
return
|
||
|
}
|
||
|
uri, err := url.Parse(location)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
//todo: add user report
|
||
|
// http://info.bilibili.co/pages/viewpage.action?pageId=8474335
|
||
|
uInfo := &report.UserInfo{
|
||
|
Mid: mid,
|
||
|
Platform: "bfs-upload-interface",
|
||
|
Build: 1,
|
||
|
Business: 61, //bfs-upload
|
||
|
Action: action.String(),
|
||
|
Ctime: time.Now(),
|
||
|
Index: []interface{}{location, up.Bucket, up.Dir, uri.Path}, // path is filename
|
||
|
Content: map[string]interface{}{
|
||
|
"upload_param": up,
|
||
|
},
|
||
|
}
|
||
|
report.User(uInfo)
|
||
|
|
||
|
result = &model.Result{
|
||
|
Location: location,
|
||
|
Etag: etag,
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// UploadAdminRecord no dir limit upload method.
|
||
|
func (s *Service) UploadAdminRecord(ctx context.Context, action model.UploadActionType, up *model.UploadParam, data []byte) (result *model.Result, err error) {
|
||
|
var (
|
||
|
location, etag string
|
||
|
b *model.Bucket
|
||
|
ok bool
|
||
|
)
|
||
|
if b, ok = s.bucketCache[up.Bucket]; !ok {
|
||
|
err = ecode.BfsUplaodBucketNotExist
|
||
|
log.Error("read bucket items failed: (%s)", up.Bucket)
|
||
|
return
|
||
|
}
|
||
|
if up.ContentType == "" {
|
||
|
up.ContentType = http.DetectContentType(data)
|
||
|
}
|
||
|
if location, etag, err = s.bfs.Upload(ctx, b.KeyID, b.KeySecret, up.ContentType, up.Bucket, up.Dir, up.FileName, data); err != nil {
|
||
|
return
|
||
|
}
|
||
|
uri, err := url.Parse(location)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
//todo: add user report
|
||
|
// http://info.bilibili.co/pages/viewpage.action?pageId=8474335
|
||
|
uInfo := &report.UserInfo{
|
||
|
Mid: 0,
|
||
|
Platform: "bfs-upload-interface",
|
||
|
Build: 1,
|
||
|
Business: 61, //bfs-upload
|
||
|
Action: action.String(), //操作类型
|
||
|
Ctime: time.Now(),
|
||
|
Index: []interface{}{location, up.Bucket, up.Dir, uri.Path}, // path is filename in bfs
|
||
|
Content: map[string]interface{}{
|
||
|
"upload_param": up,
|
||
|
},
|
||
|
}
|
||
|
report.User(uInfo)
|
||
|
|
||
|
result = &model.Result{
|
||
|
Location: location,
|
||
|
Etag: etag,
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (s *Service) dirlimit(bucket, dir, contentType string, data []byte) (err error) {
|
||
|
var (
|
||
|
width int
|
||
|
height int
|
||
|
dirlimit *model.DirConfig
|
||
|
ok bool
|
||
|
)
|
||
|
dir = strings.Trim(dir, "/")
|
||
|
if dirlimit, ok = s.bucketCache[bucket].DirLimit[dir]; !ok {
|
||
|
return
|
||
|
}
|
||
|
if len(dirlimit.Pic.AllowTypeSlice) != 0 {
|
||
|
var isAllow bool
|
||
|
for _, ctype := range dirlimit.Pic.AllowTypeSlice {
|
||
|
if ctype == contentType {
|
||
|
isAllow = true
|
||
|
}
|
||
|
}
|
||
|
if !isAllow {
|
||
|
log.Error("image content type illegal,bucket(%s),dir(%s),content type(%s)", bucket, dir, contentType)
|
||
|
err = ecode.BfsUploadFileContentTypeIllegal
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
if dirlimit.Pic.FileSize > 0 && len(data) > dirlimit.Pic.FileSize {
|
||
|
log.Error("data is too large, bucket(%s), dir(%s)", bucket, dir)
|
||
|
err = ecode.FileTooLarge
|
||
|
return
|
||
|
}
|
||
|
dataReader := bytes.NewReader(data)
|
||
|
if width, height, err = Pixel(dataReader); err != nil {
|
||
|
log.Error("get pixal error(%v), content-type maybe not support, bucket(%s), dir(%s)", err, bucket, dir)
|
||
|
err = nil
|
||
|
return
|
||
|
}
|
||
|
if (dirlimit.Pic.MinPixelWidthSize != 0 && width < dirlimit.Pic.MinPixelWidthSize) || (dirlimit.Pic.MaxPixelWidthSize != 0 && width > dirlimit.Pic.MaxPixelWidthSize) {
|
||
|
log.Error("image width illegal, bucket(%s), dir(%s)", bucket, dir)
|
||
|
err = ecode.BfsUploadFilePixelWidthIllegal
|
||
|
return
|
||
|
}
|
||
|
if (dirlimit.Pic.MinPixelWidthSize != 0 && height < dirlimit.Pic.MinPixelHeightSize) || (dirlimit.Pic.MaxPixelWidthSize != 0 && height > dirlimit.Pic.MaxPixelHeightSize) {
|
||
|
log.Error("image height illegal, bucket(%s), dir(%s)", bucket, dir)
|
||
|
err = ecode.BfsUploadFilePixelHeightIllegal
|
||
|
return
|
||
|
}
|
||
|
if dirlimit.Pic.MinAspectRatio != 0 && float64(width/height) < dirlimit.Pic.MinAspectRatio {
|
||
|
log.Error("image MinAspectRatio illegal, bucket(%s), dir(%s)", bucket, dir)
|
||
|
err = ecode.BfsUploadFilePixelAspectRatioIllegal
|
||
|
return
|
||
|
}
|
||
|
if dirlimit.Pic.MaxAspectRatio != 0 && float64(width/height) > dirlimit.Pic.MaxAspectRatio {
|
||
|
log.Error("image MaxAspectRatio illegal, bucket(%s), dir(%s)", bucket, dir)
|
||
|
err = ecode.BfsUploadFilePixelAspectRatioIllegal
|
||
|
return
|
||
|
}
|
||
|
return
|
||
|
}
|