package upload import ( "bytes" "encoding/base64" "encoding/json" "errors" "fmt" "fonchain-fiee/pkg/config" "fonchain-fiee/pkg/e" "fonchain-fiee/pkg/logic" "fonchain-fiee/pkg/model" "fonchain-fiee/pkg/service" "fonchain-fiee/pkg/utils" "github.com/disintegration/imaging" "github.com/fonchain_enterprise/utils/objstorage" "github.com/gin-gonic/gin" uuid "github.com/satori/go.uuid" "io" ffmpeg "github.com/u2takey/ffmpeg-go" "go.uber.org/zap" "io/ioutil" "mime/multipart" "net/url" "os" "path" "path/filepath" "strconv" "strings" ) 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 { service.Error(c, err) return } } zap.L().Info("UploadImg 2 ", zap.Any("mask", mask)) // 检验参数 if mask == "" || source == "" { service.Error(c, err) return } file, err := c.FormFile("file") // 检验文件 if err != nil { zap.L().Error("Upload FormFile err", zap.Error(err)) service.Error(c, err) 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 { service.Error(c, err) return } if urlParam != "" { oriUrl = fmt.Sprintf("%s?%s", oriUrl, urlParam) } service.Success(c, oriUrl) return } //创建文件名 fileExt := strings.ToLower(path.Ext(file.Filename)) if defineFileName != "" { fileFullName = defineFileName } else { newUu, _err := uuid.NewV4() if _err != nil { service.Error(c, err) 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 { service.Error(c, err) return } dst := fmt.Sprintf("%s/%s", imgPath, fileFullName) // 保存文件至指定路径 err = c.SaveUploadedFile(file, dst) if err != nil { service.Error(c, err) return } 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 { service.Error(c, err) return } data["compress_url"] = compressUrl } // 如果是视频需要截图图片做封面 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)) service.Error(c, err) return } 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)) service.Error(c, err) return } //ResponseQuickMsg(c, e.Ok, e.GetMsg(e.SUCCESS), data) //return } ossUrl, err := PutBos(dst, mediaType, true) if err != nil { service.Error(c, err) return } if urlParam != "" { ossUrl = fmt.Sprintf("%s?%s", ossUrl, urlParam) } data["ori_url"] = ossUrl service.Success(c, 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 } // 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 } 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 } 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 }