package api import ( "crypto/md5" "encoding/hex" "fmt" "image" "image/gif" "image/jpeg" "image/png" "io" "log" "mime/multipart" "net/http" "os" "path" "strconv" "strings" "github.com/pkg/errors" fs "github.com/axetroy/go-fs" "github.com/gin-gonic/gin" "github.com/nfnt/resize" "kpt.xdmy/apiserver/routers/restful" "kpt.xdmy/pkg/app" "kpt.xdmy/pkg/e" "kpt.xdmy/pkg/logging" "kpt.xdmy/pkg/setting" ) // 支持的图片后缀名 var supportImageExtNames = []string{".jpg", ".jpeg", ".png", ".ico", ".svg", ".bmp", ".gif"} /** check a file is a image or not */ func isImage(extName string) bool { for i := 0; i < len(supportImageExtNames); i++ { if supportImageExtNames[i] == extName { return true } } return false } /** Handler the parse error */ func parseFormFail(context *gin.Context) { context.JSON(http.StatusBadRequest, gin.H{ "message": "Can not parse form", }) } /** Upload file handler */ func UploadFile(context *gin.Context) { appG := app.Gin{C: context} dictId := context.Param("id") dictName := context.Param("name") logging.Info("UploadFile ", context.Keys, dictId, dictName) var ( isSupportFile bool maxUploadSize = setting.AppSetting.FileMaxSize // 最大上传大小 allowTypes = setting.AppSetting.FileAllowType // 可上传的文件类型 distPath string // 最终的输出目录 err error file *multipart.FileHeader src multipart.File dist *os.File ) // Source if file, err = context.FormFile("file"); err != nil { parseFormFail(context) return } sqlname := context.GetHeader("optname") extname := path.Ext(file.Filename) if len(allowTypes) != 0 { for i := 0; i < len(allowTypes); i++ { if allowTypes[i] == extname { isSupportFile = true break } } if isSupportFile == false { context.JSON(http.StatusBadRequest, gin.H{ "message": "不支持的文件类型: " + extname, }) return } } if file.Size > int64(maxUploadSize) { context.JSON(http.StatusBadRequest, gin.H{ "message": "上传文件太大, 最大限制为" + strconv.Itoa(int(maxUploadSize)/1048576) + "(M): ", }) return } if src, err = file.Open(); err != nil { // open the file fail... } defer src.Close() hash := md5.New() io.Copy(hash, src) md5string := hex.EncodeToString(hash.Sum([]byte(""))) fileName := md5string + extname // Destination filePath := setting.CurrentPath + setting.AppSetting.FileSavePath + dictName + "/" err = PathCheck(filePath) //检查路径并创建 if err != nil { fmt.Println("PathCheck err", err) } distPath = path.Join(filePath, fileName) if dist, err = os.Create(distPath); err != nil { fmt.Println("Create err", err) } defer dist.Close() //distPath = setting.CurrentPath + setting.AppSetting.FileSavePath +"/"+ fileName if dist, err = os.Create(distPath); err != nil { // create dist file fail... } defer dist.Close() // FIXME: open 2 times if src, err = file.Open(); err != nil { // } // Copy io.Copy(dist, src) var execresult interface{} params := context.Request.Form if sqlname != "" { sql, p := restful.GetSqlByNameDB(sqlname) //todo insertcustomdoc if sql != "" { s_params := make([]interface{}, 0) paramslist := strings.Split(p, ",") if len(paramslist) > 0 && p != "" { for _, value := range paramslist { fmt.Println("s_params value", s_params, value) if strings.ToLower(strings.Trim(value, " ")) == "username" { //picpath, picname, username, newpicname tempv := params.Get("jwt_username") s_params = append(s_params, tempv) } else if strings.ToLower(strings.Trim(value, " ")) == "docname" { s_params = append(s_params, file.Filename) } else if strings.ToLower(strings.Trim(value, " ")) == "newdocname" { s_params = append(s_params, fileName) } else if strings.ToLower(strings.Trim(value, " ")) == "docpath" { s_params = append(s_params, dictName) // } else if strings.ToLower(strings.Trim(value, " ")) == "dictid" { s_params = append(s_params, dictId) // fmt.Println("s_params", s_params, dictId) } else if strings.ToLower(strings.Trim(value, " ")) == "filesize" { s_params = append(s_params, file.Size) // } else { s_params = append(s_params, params.Get(strings.Trim(value, " "))) } } } execresult, err = execDataBySql(sql, s_params) if err != nil { fmt.Println("execDataBySql err", err) appG.Response(http.StatusOK, e.ERROR, err.Error()) } } } context.JSON(http.StatusOK, gin.H{ "hash": md5string, "filename": fileName, "origin": file.Filename, "size": file.Size, "execresult": execresult, }) } /** Upload image handler */ func UploaderImage(context *gin.Context) { logging.Info("UploaderImage ", context.Keys) var ( maxUploadSize = setting.AppSetting.ImageMaxSize // 最大上传大小 distPath string // 最终的输出目录 err error file *multipart.FileHeader src multipart.File dist *os.File ) // Source if file, err = context.FormFile("file"); err != nil { parseFormFail(context) return } sqlname := context.GetHeader("optname") extname := strings.ToLower(path.Ext(file.Filename)) if isImage(extname) == false { context.JSON(http.StatusBadRequest, gin.H{ "message": "不支持的上传文件类型: " + extname, }) return } if file.Size > int64(maxUploadSize) { context.JSON(http.StatusBadRequest, gin.H{ "message": "上传文件太大, 最大文件大小限制为(byte): " + strconv.Itoa(int(maxUploadSize)), }) return } if src, err = file.Open(); err != nil { } defer src.Close() hash := md5.New() io.Copy(hash, src) md5string := hex.EncodeToString(hash.Sum([]byte(""))) fileName := md5string + extname picPath := setting.CurrentPath + setting.AppSetting.ImageSavePath + sqlname + "/" err = PathCheck(picPath) //检查路径并创建 if err != nil { fmt.Println("PathCheck err", err) } distPath = path.Join(picPath, fileName) if dist, err = os.Create(distPath); err != nil { fmt.Println("Create err", err) } defer dist.Close() // FIXME: open 2 times if src, err = file.Open(); err != nil { // } defer src.Close() // Copy io.Copy(dist, src) // 压缩缩略图 // 不管成功与否,都会进行下一步的返回 if _, err := thumbnailify(distPath, sqlname); err != nil { logging.Error("thumbnailify_err", err) picThumbnailPath := setting.CurrentPath + setting.AppSetting.ThumbnailSavePath + sqlname + "/" println(picThumbnailPath) err = PathCheck(picThumbnailPath) //检查路径并创建 distThumbnailPath := path.Join(picThumbnailPath, fileName) println(distThumbnailPath) distThumbnail, err := os.Create(distThumbnailPath) if err != nil { fmt.Println("CreateThumbnail err", err) } srcThumbnail, err := file.Open() if err != nil { // } io.Copy(distThumbnail, srcThumbnail) distThumbnail.Close() src.Close() } var execresult interface{} params := context.Request.Form if sqlname != "" { sql, p := restful.GetSqlByNameDB(sqlname) if sql != "" { s_params := make([]interface{}, 0) paramslist := strings.Split(p, ",") if len(paramslist) > 0 && p != "" { for _, value := range paramslist { if strings.ToLower(strings.Trim(value, " ")) == "username" { //picpath, picname, username, newpicname tempv := params.Get("jwt_username") s_params = append(s_params, tempv) } else if strings.ToLower(strings.Trim(value, " ")) == "picname" { s_params = append(s_params, file.Filename) } else if strings.ToLower(strings.Trim(value, " ")) == "newpicname" { s_params = append(s_params, fileName) } else if strings.ToLower(strings.Trim(value, " ")) == "picpath" { s_params = append(s_params, sqlname) //全路径加文件名 } else { s_params = append(s_params, params.Get(strings.Trim(value, " "))) } } } execresult, err = execDataBySql(sql, s_params) if err != nil { fmt.Println("execDataBySql err", err) } } } context.JSON(http.StatusOK, gin.H{ "hash": md5string, "filename": fileName, "origin": file.Filename, "size": file.Size, "execresult": execresult, }) } /** Get file raw */ func GetFileRaw(context *gin.Context) { filename := context.Param("filename") logging.Info("GetFileRaw ", context.Keys, filename) filePath := path.Join(setting.CurrentPath, setting.AppSetting.FileSavePath, filename) if isExistFile := fs.PathExists(filePath); isExistFile == false { // if the path not found http.NotFound(context.Writer, context.Request) return } http.ServeFile(context.Writer, context.Request, filePath) } /** Download a file */ func DownloadFile(context *gin.Context) { filename := context.Param("filename") logging.Info("DownloadFile ", context.Keys, filename) sqlstr := fmt.Sprintf("SELECT docpath ,newdocname FROM eq_doc where id = %s ", filename) var docpath, newdocname string err := restful.BasicList(sqlstr, nil, &docpath, &newdocname) if err != nil { log.Default().Printf("DownloadFile queryx:%v", err) } originFilePath := path.Join(setting.CurrentPath, setting.AppSetting.FileSavePath, docpath, newdocname) if fs.PathExists(originFilePath) == false { http.NotFound(context.Writer, context.Request) return } http.ServeFile(context.Writer, context.Request, originFilePath) } /** Get Origin image */ func GetOriginImage(context *gin.Context) { //appG := app.Gin{C: context} filename := context.Param("filename") logging.Info("GetOriginImage ", context.Keys, filename) sqlstr := fmt.Sprintf("SELECT picpath,newpicname FROM eq_pic where id = %s ", filename) var picpath, newpicname string err := restful.BasicList(sqlstr, nil, &picpath, &newpicname) if err != nil { log.Default().Printf("GetOriginImage queryx eq_pic: %v", err) } if picpath == "" && newpicname == "" { http.NotFound(context.Writer, context.Request) return } originImagePath := path.Join(setting.CurrentPath, setting.AppSetting.ImageSavePath, picpath, newpicname) if fs.PathExists(originImagePath) == false { // if the path not found http.NotFound(context.Writer, context.Request) return } http.ServeFile(context.Writer, context.Request, originImagePath) //appG.Response(http.StatusOK, e.SUCCESS, eqpic[0]["picname"]) } /** Get thumbnail image */ func GetThumbnailImage(context *gin.Context) { filename := context.Param("filename") logging.Info("GetThumbnailImage ", context.Keys, filename) sqlstr := fmt.Sprintf("SELECT picpath,newpicname FROM eq_pic where id = %s ", filename) var picpath, newpicname string err := restful.BasicList(sqlstr, nil, &picpath, &newpicname) if err != nil { log.Default().Printf("GetOriginImage queryx eq_pic: %v", err) } if newpicname == "" { http.NotFound(context.Writer, context.Request) return } thumbnailImagePath := path.Join(setting.CurrentPath, setting.AppSetting.ThumbnailSavePath, picpath, newpicname) originImagePath := path.Join(setting.CurrentPath, setting.AppSetting.ImageSavePath, filename) if fs.PathExists(thumbnailImagePath) == false { // if thumbnail image not exist, try to get origin image if fs.PathExists(originImagePath) == true { http.ServeFile(context.Writer, context.Request, originImagePath) return } // if the path not found http.NotFound(context.Writer, context.Request) return } http.ServeFile(context.Writer, context.Request, thumbnailImagePath) } /** Generate thumbnail */ func thumbnailify(imagePath, subdirectory string) (outputPath string, err error) { var ( file *os.File img image.Image ) Filename := strings.ToLower(path.Base(imagePath)) extname := strings.ToLower(path.Ext(imagePath)) outputPath = setting.CurrentPath + setting.AppSetting.ThumbnailSavePath + subdirectory + "/" + Filename err = PathCheck(setting.CurrentPath + setting.AppSetting.ThumbnailSavePath + subdirectory) if err != nil { fmt.Println("thumbnailify PathCheck err", err) } // 读取文件 if file, err = os.Open(imagePath); err != nil { return } defer file.Close() // decode jpeg into image.Image switch extname { case ".jpg", ".jpeg": img, err = jpeg.Decode(file) break case ".png": img, err = png.Decode(file) break case ".gif": img, err = gif.Decode(file) break default: err = errors.New("Unsupport file type" + extname) return } if img == nil { err = errors.New("Generate thumbnail fail...") return } m := resize.Thumbnail(uint(setting.AppSetting.ThumbnailMaxWidth), uint(setting.AppSetting.ThumbnailMaxHeight), img, resize.Lanczos3) out, err := os.Create(outputPath) if err != nil { return } defer out.Close() // write new image to file //decode jpeg/png/gif into image.Image switch extname { case ".jpg", ".jpeg": jpeg.Encode(out, m, nil) break case ".png": png.Encode(out, m) break case ".gif": gif.Encode(out, m, nil) break default: err = errors.New("Unsupport file type" + extname) return } return } func UploadFiles(c *gin.Context) { logging.Info("UploadFiles ", c.Keys) appG := app.Gin{C: c} err := c.Request.ParseMultipartForm(200000) if err != nil { appG.Response(http.StatusOK, e.ERROR_IMPORT_FAIL, err) return } // 获取表单 form := c.Request.MultipartForm // 获取参数upload后面的多个文件名,存放到数组files里面, files := form.File["upload"] // 遍历数组,每取出一个file就拷贝一次 for i := range files { file, err := files[i].Open() defer file.Close() if err != nil { appG.Response(http.StatusOK, e.ERROR_IMPORT_FAIL, err) return } fileName := files[i].Filename fmt.Println(fileName) out, err := os.Create(fileName) defer out.Close() if err != nil { appG.Response(http.StatusOK, e.ERROR_IMPORT_FAIL, err) return } _, err = io.Copy(out, file) if err != nil { appG.Response(http.StatusOK, e.ERROR_IMPORT_FAIL, err) return } appG.Response(http.StatusOK, e.SUCCESS, "upload successful \n") } } func PathCheck(path string) (err error) { b, err := PathExists(path) if err != nil { fmt.Println("exist err", err) } if !b { fmt.Println(path, " 目录不存在,重新创建") err = os.Mkdir(path, 0777) if err != nil { fmt.Println("Mkdir err", err) } } return } func PathExists(path string) (bool, error) { _, err := os.Stat(path) if err == nil { return true, nil } if os.IsNotExist(err) { return false, nil } return false, err }