fonchain-fiee/pkg/service/upload.go
2025-02-19 14:24:15 +08:00

455 lines
14 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/url"
"github.com/fonchain_enterprise/fonchain-main/pkg/logic"
"github.com/fonchain_enterprise/fonchain-main/pkg/model"
"github.com/disintegration/imaging"
"mime/multipart"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"sync"
"dubbo.apache.org/dubbo-go/v3/common/logger"
"github.com/fonchain_enterprise/fonchain-main/pkg/config"
"github.com/fonchain_enterprise/fonchain-main/pkg/e"
"github.com/fonchain_enterprise/fonchain-main/pkg/serializer"
"github.com/fonchain_enterprise/fonchain-main/pkg/utils"
"github.com/fonchain_enterprise/utils/objstorage"
"github.com/gin-gonic/gin"
uuid "github.com/satori/go.uuid"
ffmpeg "github.com/u2takey/ffmpeg-go"
"go.uber.org/zap"
)
var (
wg sync.WaitGroup
)
const (
MediaPath = "./runtime/"
RouteType = "static/"
VideoType = "video"
ImageType = "image"
PngType = "png"
ArtworkFilePath = "artwork"
ArtworkChunkBasePath = "./runtime/tmp/artworks"
)
func UploadImg(c *gin.Context) {
var err error
var filename string
var fileFullName string
source := c.PostForm("source")
mask := c.PostForm("mask")
action := c.PostForm("action")
defineFileName := c.PostForm("defineFileName")
urlParam := c.PostForm("urlParam")
if mask == "" {
mask = "default"
}
mediaType := c.PostForm("type")
zap.L().Info("UploadImg 1", zap.Any("mask", mask))
var BasePath string
if mediaType == "" || mediaType == ImageType {
mediaType = ImageType
}
BasePath = fmt.Sprintf("%s%s", MediaPath, mediaType)
//BaseRoute = fmt.Sprintf("%s%s", RouteType, mediaType)
var isCompress int
if cStr, ok := c.GetPostForm("is_compress"); ok {
var errS error
isCompress, errS = strconv.Atoi(cStr)
if errS != nil {
ResponseQuickMsg(c, e.Failed, errS.Error(), nil)
return
}
}
zap.L().Info("UploadImg 2 ", zap.Any("mask", mask))
// 检验参数
if mask == "" || source == "" {
ResponseQuickMsg(c, e.Failed, e.GetMsg(e.InvalidParams), nil)
return
}
file, err := c.FormFile("file")
// 检验文件
if err != nil {
zap.L().Error("Upload FormFile err", zap.Error(err))
ResponseQuickMsg(c, e.Failed, err.Error(), nil)
return
}
logger.Errorf("UploadImg 3 %+v", mask)
// 判断是不是视频或者需要压缩
var oriUrl string
if isCompress != 1 && mediaType != "video" && action == "" {
oriUrl, err = quickBos(file, mediaType, mask, source, defineFileName)
if err != nil {
ResponseQuickMsg(c, e.Failed, err.Error(), nil)
return
}
if urlParam != "" {
oriUrl = fmt.Sprintf("%s?%s", oriUrl, urlParam)
}
ResponseQuickMsg(c, e.Ok, e.GetMsg(e.Success), map[string]interface{}{
"ori_url": oriUrl,
})
return
}
logger.Errorf("UploadImg 4 %+v", mask)
//创建文件名
fileExt := strings.ToLower(path.Ext(file.Filename))
if defineFileName != "" {
fileFullName = defineFileName
} else {
newUu, _err := uuid.NewV4()
if _err != nil {
ResponseQuickMsg(c, e.Failed, _err.Error(), nil)
return
}
filename = newUu.String()
fileFullName = fmt.Sprintf("%s%s", filename, fileExt)
}
//检测文件夹 不存在就创建
imgPath := fmt.Sprintf("%s/%s/%s", BasePath, source, mask)
_, err = utils.CheckDirPath(imgPath, true)
if err != nil {
ResponseQuickMsg(c, e.Failed, err.Error(), nil)
return
}
dst := fmt.Sprintf("%s/%s", imgPath, fileFullName)
logger.Errorf("UploadImg 5 %+v", mask)
// 保存文件至指定路径
err = c.SaveUploadedFile(file, dst)
if err != nil {
logger.Errorf("Upload FormFile err", err)
ResponseQuickMsg(c, e.Failed, err.Error(), nil)
return
}
logger.Errorf("UploadImg 6 %+v", mask)
if action == model.ImgActionRotate {
fileFullName = fmt.Sprintf("%s%s", filename, fileExt)
newDst := fmt.Sprintf("%s/%s_rotate%v", imgPath, filename, fileExt)
if err = logic.MakeThumbnail(dst, newDst); err != nil {
//ResponseQuickMsg(c, e.Failed, e.GetMsg(e.ERROR_ROTATE_IMG), nil)
//return
} else {
_ = os.Remove(dst)
dst = newDst
}
}
//localUrl := fmt.Sprintf("%s/%s/%s/%s/%s", config.ServerDM, BaseRoute, source, mask, fileFullName)
var data map[string]string = make(map[string]string, 2)
//data["ori_url"] = localUrl
if int32(isCompress) == 1 {
//压缩图片并存储在原图路径命名格式xx.jpg_small.jpg
fileFullName = fmt.Sprintf("%s_small%s", filename, fileExt)
newDst := fmt.Sprintf("%s/%s", imgPath, fileFullName)
//compressUrl := fmt.Sprintf("%s/%s/%s/%s/%s", config.ServerDM, BaseRoute, source, mask, fileFullName)
err = utils.CompressJPG(dst, newDst)
compressUrl, err := PutBos(newDst, mediaType, true)
if err != nil {
logger.Errorf("Upload compress err", err)
ResponseQuickMsg(c, e.Failed, err.Error(), data)
return
}
data["compress_url"] = compressUrl
}
logger.Errorf("UploadImg 7 %+v", mask)
// 如果是视频需要截图图片做封面
if mediaType == VideoType {
videoCover := fmt.Sprintf("%s/%s", imgPath, filename)
_, err = GetSnapshot(dst, videoCover, 1)
if err != nil {
zap.L().Error("GetSnapshot err", zap.Error(err))
ResponseQuickMsg(c, e.Failed, "获取封面失败", err.Error())
return
}
logger.Errorf("UploadImg 8 %+v", mask)
zap.L().Info("UploadImg 8.1 videoCover", zap.Any("videoCover", videoCover))
//data["cover_url"] = fmt.Sprintf("%s/%s/%s/%s/%s", config.ServerDM, BaseRoute, source, mask, fmt.Sprintf("%s.%s", filename, PngType))
coverUrl, err := PutBos(videoCover+"."+PngType, mediaType, true)
if urlParam != "" {
coverUrl = fmt.Sprintf("%s?%s", coverUrl, urlParam)
}
data["cover_url"] = coverUrl
if err != nil {
zap.L().Error("Upload GetSnapshot err", zap.Error(err))
ResponseQuickMsg(c, e.Failed, err.Error(), nil)
return
}
//ResponseQuickMsg(c, e.Ok, e.GetMsg(e.SUCCESS), data)
//return
}
ossUrl, err := PutBos(dst, mediaType, true)
if err != nil {
ResponseQuickMsg(c, e.Failed, err.Error(), nil)
return
}
if urlParam != "" {
ossUrl = fmt.Sprintf("%s?%s", ossUrl, urlParam)
}
data["ori_url"] = ossUrl
ResponseQuickMsg(c, e.Ok, e.GetMsg(e.SUCCESS), data)
return
}
func quickBos(file *multipart.FileHeader, mediaType string, mask string, source string, defineFileName string) (url string, err error) {
newFile, _ := file.Open()
var filename string
defer newFile.Close()
if defineFileName != "" {
filename = defineFileName
} else {
uuids, _ := uuid.NewV4()
filename = uuids.String()
filename = fmt.Sprintf("%s%s", filename, filepath.Ext(file.Filename))
}
filePath := fmt.Sprintf("%s/%s/%s/%s", mediaType, mask, source, filename)
fileBytes, _ := ioutil.ReadAll(newFile)
if mediaType == "image" {
if err = BaiduCheckImage(fileBytes); err != nil {
return
}
}
var objectName string = fmt.Sprintf("%s/%s/%s", config.ConfigData.Oss.BaseDir, config.Env, filePath)
BOSClient, _ := objstorage.NewOSS(config.ConfigData.Oss.AccessKeyId, config.ConfigData.Oss.AccessKeySecret, config.ConfigData.Oss.Endpoint)
_, err = BOSClient.PutObjectFromBytes(config.ConfigData.Oss.BucketName, objectName, fileBytes)
if err != nil {
logger.Errorf("quickOss err", err)
return
}
//url = fmt.Sprintf("%s%s%s/%s", config.BosHttp, config.BosBucketName, config.BosUrl, objectName)
url = fmt.Sprintf("%s/%s", config.ConfigData.Oss.CdnHost, objectName)
return
}
// UploadMulti 批量上传
func UploadMulti(c *gin.Context) {
form, err := c.MultipartForm()
source := c.PostForm("source")
mask := c.PostForm("mask")
mediaType := c.PostForm("type")
if mask == "" {
mask = "0"
}
if mediaType == "" || mediaType == "image" {
mediaType = "image"
}
if source == "" {
ResponseQuickMsg(c, e.Failed, e.GetMsg(e.InvalidParams), nil)
return
}
if err != nil {
logger.Errorf("UploadMulti err", err)
ResponseMsg(c, e.InvalidParams, serializer.Response{
Msg: err.Error(),
Code: e.Failed,
Error: err,
})
return
}
var data = make(map[string]string, len(form.File))
wg.Add(len(form.File))
var fileCh = make(chan string, len(form.File))
for _, files := range form.File {
for _, file := range files {
go func() {
defer wg.Done()
var uploadInfo model.UploadInfo
disp := file.Header["Content-Disposition"]
if len(disp) > 0 {
dispKv := strings.Split(disp[0], ";")
for _, vv := range dispKv {
vv = strings.Trim(vv, " ")
if vv[:4] == "name" {
uploadInfo.FileKName = vv[strings.Index(vv, "\"")+1 : strings.LastIndex(vv, "\"")]
}
}
}
newFile, _ := file.Open()
defer newFile.Close()
uuids, _ := uuid.NewV4()
filePath := fmt.Sprintf("%s/%s/%s/%s%s", mediaType, mask, source, uuids, filepath.Ext(file.Filename))
fileBytes, _ := ioutil.ReadAll(newFile)
if mediaType == "image" {
if err = BaiduCheckImage(fileBytes); err != nil {
uploadInfo.Err = err.Error()
return
}
}
var objectName string = fmt.Sprintf("%s/%s/%s", config.ConfigData.Oss.BaseDir, config.Env, filePath)
BOSClient, _ := objstorage.NewOSS(config.ConfigData.Oss.AccessKeyId, config.ConfigData.Oss.AccessKeySecret, config.ConfigData.Oss.Endpoint)
_, err = BOSClient.PutObjectFromBytes(config.ConfigData.Oss.BucketName, objectName, fileBytes)
if err != nil {
logger.Errorf("quickBos err", err)
return
}
//uploadInfo.Url = fmt.Sprintf("%s%s%s/%s", config.BosHttp, config.BosBucketName, config.BosUrl, objectName)
uploadInfo.Url = fmt.Sprintf("%s/%s", config.ConfigData.Oss.CdnHost, objectName)
infoByte, _ := json.Marshal(uploadInfo)
fileCh <- string(infoByte)
}()
}
}
wg.Wait()
close(fileCh)
for v := range fileCh {
if v != "" {
var uploadInfo model.UploadInfo
if err = json.Unmarshal([]byte(v), &uploadInfo); err == nil {
data[uploadInfo.FileKName] = uploadInfo.Url
}
}
}
ResponseMsg(c, e.UpdatePasswordSuccess, serializer.Response{
Msg: e.GetMsg(e.SUCCESS),
Status: e.Ok,
Data: data,
})
}
func PutBos(filePath string, mediaType string, needRemove bool) (url string, err error) {
BOSClient, err := objstorage.NewOSS(config.ConfigData.Oss.AccessKeyId, config.ConfigData.Oss.AccessKeySecret, config.ConfigData.Oss.Endpoint)
if err != nil {
logger.Errorf("PutBos NewOss err ", err)
err = errors.New(e.GetMsg(e.ErrorUploadBos))
return
}
f, err := os.Open(filePath)
if err != nil {
logger.Errorf("PutBos Open err %+v", err.Error())
return
}
fileBytes, _ := io.ReadAll(f)
f.Close()
//删除本地文件
if needRemove {
os.Remove(filePath)
}
if mediaType == "image" {
if err = BaiduCheckImage(fileBytes); err != nil {
return
}
}
filePath = strings.Replace(filePath, "./runtime", "", 1)
var objectName string = fmt.Sprintf("%s/%s%s", config.ConfigData.Oss.BaseDir, config.Env, filePath)
_, err = BOSClient.PutObjectFromBytes(config.ConfigData.Oss.BucketName, objectName, fileBytes)
if err != nil {
logger.Errorf("PutBos PutObject err %+v", err.Error())
err = errors.New(e.GetMsg(e.ErrorUploadBos))
return
}
//url = fmt.Sprintf("%s%s%s/%s", config.BosHttp, config.BosBucketName, config.BosUrl, objectName)
url = fmt.Sprintf("%s/%s", config.ConfigData.Oss.CdnHost, objectName)
return
}
// PutBosWithName 自定义osspath
func PutBosWithName(filePath string, needRemove bool, ossPath string) (url string, err error) {
BOSClient, err := objstorage.NewOSS(config.ConfigData.Oss.AccessKeyId, config.ConfigData.Oss.AccessKeySecret, config.ConfigData.Oss.Endpoint)
if err != nil {
logger.Errorf("PutBosWithName err1 ", err)
err = errors.New(e.GetMsg(e.ErrorUploadBos))
return
}
if ossPath == "" {
ossPath = filePath[1:]
}
var objectName string = fmt.Sprintf("%s/%s%s", config.ConfigData.Oss.BaseDir, config.Env, ossPath)
_, err = BOSClient.PutObject(config.ConfigData.Oss.BucketName, objectName, filePath)
if err != nil {
logger.Errorf("PutBosWithName err2 ", err)
err = errors.New(e.GetMsg(e.ErrorUploadBos))
return
}
//删除本地文件
if needRemove {
_ = os.Remove(filePath)
}
//url = fmt.Sprintf("%s%s%s/%s", config.BosHttp, config.BosBucketName, config.BosUrl, objectName)
url = fmt.Sprintf("%s/%s", config.ConfigData.Oss.CdnHost, objectName)
return
}
func GetSnapshot(videoPath, snapshotPath string, frameNum int) (snapshotName string, err error) {
buf := bytes.NewBuffer(nil)
zap.L().Info("GetSnapshot", zap.Any("videoPath", videoPath))
err = ffmpeg.Input(videoPath).
Filter("select", ffmpeg.Args{fmt.Sprintf("gte(n,%d)", frameNum)}).
Output("pipe:", ffmpeg.KwArgs{"vframes": 1, "format": "image2", "vcodec": "mjpeg"}).
WithOutput(buf, os.Stdout).
Run()
if err != nil {
zap.L().Error("GetSnapshot Input err", zap.Error(err))
return "", err
}
img, err := imaging.Decode(buf)
if err != nil {
zap.L().Error("GetSnapshot Decode err", zap.Error(err))
return "", err
}
err = imaging.Save(img, snapshotPath+"."+PngType)
if err != nil {
zap.L().Error("GetSnapshot Save err", zap.Error(err))
return "", err
}
names := strings.Split(snapshotPath, "\\")
snapshotName = names[len(names)-1] + "." + PngType
return
}
// BaiduCheckImage 图片鉴黄
func BaiduCheckImage(imageByte []byte) (err error) {
return
var (
accesstoken string
response string
)
sourcestring := base64.StdEncoding.EncodeToString(imageByte)
if accesstoken, err = logic.GetImageAccessToken(); err != nil {
return err
}
host := "https://aip.baidubce.com/rest/2.0/solution/v1/img_censor/v2/user_defined?access_token=[" + accesstoken + "]"
if response, err = utils.PostForm(host, url.Values{"image": {sourcestring}}); err != nil {
logger.Error("user_defined PostForm err", err)
return err
}
var res struct {
ErrorCode int64 `json:"error_code"`
ErrorMsg string `json:"error_msg"`
Conclusion string `json:"conclusion"`
Log_id uint64 `json:"log_id"`
IsHitMd5 bool `json:"isHitMd5"`
ConclusionType int64 `json:"conclusionType"`
}
if err = json.Unmarshal([]byte(response), &res); err != nil {
err = errors.New(e.GetMsg(e.JsonUnmarshal))
return
}
logger.Error("user_defined res", res)
if res.ErrorCode != 0 || res.ErrorMsg != "" {
return errors.New(e.GetMsg(e.ERROR_BAIDU_FAIL))
}
if res.Conclusion != "合规" && res.Conclusion != "疑似" {
return errors.New(e.GetMsg(e.ERROR_BAIDU_IMAGE))
}
return nil
}