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.
282 lines
8.0 KiB
282 lines
8.0 KiB
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 |
|
}
|
|
|