package common import ( "bytes" "encoding/base64" "encoding/json" "errors" "fmt" "github.com/disintegration/imaging" "github.com/dubbogo/gost/log/logger" "github.com/exhibition-main/internal/config" "github.com/exhibition-main/internal/model" "github.com/exhibition-main/internal/msg" "github.com/exhibition-main/internal/response" "github.com/exhibition-main/pkg/logic" "github.com/exhibition-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" "io/ioutil" "mime/multipart" "net/url" "os" "path" "path/filepath" "strconv" "strings" "sync" ) 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 source := c.PostForm("source") mask := c.PostForm("mask") action := c.PostForm("action") if mask == "" { mask = "default" } mediaType := c.PostForm("type") logger.Errorf("UploadImg 1 %+v", 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 { response.ResponseQuickMsg(c, msg.Fail, errS.Error(), nil) return } } logger.Errorf("UploadImg 2 %+v", mask) // 检验参数 if mask == "" || source == "" { response.ResponseQuickMsg(c, msg.Fail, msg.INVALID_PARAMS, nil) return } file, err := c.FormFile("file") // 检验文件 if err != nil { logger.Errorf("Upload FormFile err", err) response.ResponseQuickMsg(c, msg.Fail, 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) if err != nil { response.ResponseQuickMsg(c, msg.Fail, err.Error(), nil) return } response.ResponseQuickMsg(c, msg.Ok, msg.SUCCESS, map[string]interface{}{ "ori_url": oriUrl, }) return } logger.Errorf("UploadImg 4 %+v", mask) //创建文件名 fileExt := strings.ToLower(path.Ext(file.Filename)) filename := uuid.NewV4() if err != nil { response.ResponseQuickMsg(c, msg.Fail, err.Error(), nil) return } fileFullName := fmt.Sprintf("%s%s", filename, fileExt) //检测文件夹 不存在就创建 imgPath := fmt.Sprintf("%s/%s/%s", BasePath, source, mask) _, err = utils.CheckDirPath(imgPath, true) if err != nil { response.ResponseQuickMsg(c, msg.Fail, 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) response.ResponseQuickMsg(c, msg.Fail, 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 = 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) response.ResponseQuickMsg(c, msg.Fail, 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 { logger.Errorf("GetSnapshot err %+v", err.Error()) response.ResponseQuickMsg(c, msg.Fail, "获取封面失败", err.Error()) return } logger.Errorf("UploadImg 8 %+v", mask) logger.Errorf("UploadImg 8.1 videoCover %+v", 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) data["cover_url"] = coverUrl if err != nil { logger.Errorf("Upload GetSnapshot err", zap.Error(err)) response.ResponseQuickMsg(c, msg.Fail, err.Error(), nil) return } } ossUrl, err := PutBos(dst, mediaType, true) if err != nil { response.ResponseQuickMsg(c, msg.Fail, err.Error(), nil) return } data["ori_url"] = ossUrl response.ResponseQuickMsg(c, msg.Ok, msg.SUCCESS, data) return } func GetSnapshot(videoPath, snapshotPath string, frameNum int) (snapshotName string, err error) { buf := bytes.NewBuffer(nil) 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 { logger.Errorf("GetSnapshot Input err:", zap.Error(err)) return "", err } img, err := imaging.Decode(buf) if err != nil { logger.Errorf("GetSnapshot Decode err:", zap.Error(err)) return "", err } err = imaging.Save(img, snapshotPath+"."+PngType) if err != nil { logger.Errorf("GetSnapshot Save err:", zap.Error(err)) return "", err } names := strings.Split(snapshotPath, "\\") snapshotName = names[len(names)-1] + "." + PngType return } func PutBos(filePath string, mediaType string, needRemove bool) (url string, err error) { BOSClient, err := objstorage.NewBOS(config.Data.Bos.AccessKeyId, config.Data.Bos.AccessKeySecret, objstorage.BOS_BJ) if err != nil { logger.Errorf("PutBos NewBOS err ", err) err = errors.New(msg.ErrorUploadBos) return } f, err := os.Open(filePath) if err != nil { logger.Errorf("PutBos Open err %+v", err.Error()) return } fileBytes, _ := ioutil.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.Data.Bos.BosBaseDir, config.Data.System.Mode, filePath) _, err = BOSClient.PutObjectFromBytes(config.Data.Bos.BucketName, objectName, fileBytes) if err != nil { logger.Errorf("PutBos PutObject err %+v", err.Error()) err = errors.New(msg.ErrorUploadBos) return } url = fmt.Sprintf("%s/%s", config.Data.Bos.CdnHost, objectName) return } func quickBos(file *multipart.FileHeader, mediaType string, mask string, source string) (url string, err error) { 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 { return } } var objectName string = fmt.Sprintf("%s/%s/%s", config.Data.Bos.BosBaseDir, config.Data.System.Mode, filePath) BOSClient, _ := objstorage.NewBOS(config.Data.Bos.AccessKeyId, config.Data.Bos.AccessKeySecret, objstorage.BOS_BJ) _, err = BOSClient.PutObjectFromBytes(config.Data.Bos.BucketName, objectName, fileBytes) if err != nil { logger.Errorf("quickBos err", err) return } url = fmt.Sprintf("%s/%s", config.Data.Bos.CdnHost, objectName) 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(msg.JSON_UNMARSHAL) return } logger.Error("user_defined res", res) if res.ErrorCode != 0 || res.ErrorMsg != "" { return errors.New(msg.ERROR_BAIDU_FAIL) } if res.Conclusion != "合规" && res.Conclusion != "疑似" { return errors.New(msg.ERROR_BAIDU_IMAGE) } return nil }