package rest import ( "fmt" "github.com/disintegration/imaging" "github.com/eyebluecn/tank/code/core" "github.com/eyebluecn/tank/code/tool/util" "image" "net/http" "os" "path/filepath" "strconv" "strings" ) //@Service type ImageCacheService struct { BaseBean imageCacheDao *ImageCacheDao userDao *UserDao matterDao *MatterDao } //初始化方法 func (this *ImageCacheService) Init() { this.BaseBean.Init() //手动装填本实例的Bean. 这里必须要用中间变量方可。 b := core.CONTEXT.GetBean(this.imageCacheDao) if b, ok := b.(*ImageCacheDao); ok { this.imageCacheDao = b } b = core.CONTEXT.GetBean(this.userDao) if b, ok := b.(*UserDao); ok { this.userDao = b } b = core.CONTEXT.GetBean(this.matterDao) if b, ok := b.(*MatterDao); ok { this.matterDao = b } } //获取某个文件的详情,会把父级依次倒着装进去。如果中途出错,直接抛出异常。 func (this *ImageCacheService) Detail(uuid string) *ImageCache { imageCache := this.imageCacheDao.CheckByUuid(uuid) return imageCache } //获取预处理时必要的参数 func (this *ImageCacheService) ResizeParams(request *http.Request) (needProcess bool, resizeMode string, resizeWidth int, resizeHeight int) { var err error //1.0 模式准备逐步废弃掉 if request.FormValue("imageProcess") == "resize" { //老模式使用 imageResizeM,imageResizeW,imageResizeH imageResizeM := request.FormValue("imageResizeM") if imageResizeM == "" { imageResizeM = "fit" } else if imageResizeM != "fit" && imageResizeM != "fill" && imageResizeM != "fixed" { panic("imageResizeM参数错误") } imageResizeWStr := request.FormValue("imageResizeW") var imageResizeW int if imageResizeWStr != "" { imageResizeW, err = strconv.Atoi(imageResizeWStr) this.PanicError(err) if imageResizeW < 1 || imageResizeW > 4096 { panic("缩放尺寸不能超过4096") } } imageResizeHStr := request.FormValue("imageResizeH") var imageResizeH int if imageResizeHStr != "" { imageResizeH, err = strconv.Atoi(imageResizeHStr) this.PanicError(err) if imageResizeH < 1 || imageResizeH > 4096 { panic("缩放尺寸不能超过4096") } } return true, imageResizeM, imageResizeW, imageResizeH } else if request.FormValue("ir") != "" { //新模式使用 mode_w_h 如果w或者h为0表示这项值不设置 imageResizeStr := request.FormValue("ir") arr := strings.Split(imageResizeStr, "_") if len(arr) != 3 { panic("参数不符合规范,格式要求为mode_w_h") } imageResizeM := arr[0] if imageResizeM == "" { imageResizeM = "fit" } else if imageResizeM != "fit" && imageResizeM != "fill" && imageResizeM != "fixed" { panic("imageResizeM参数错误") } imageResizeWStr := arr[1] var imageResizeW int if imageResizeWStr != "" { imageResizeW, err = strconv.Atoi(imageResizeWStr) this.PanicError(err) if imageResizeW < 0 || imageResizeW > 4096 { panic("缩放尺寸不能超过4096") } } imageResizeHStr := arr[2] var imageResizeH int if imageResizeHStr != "" { imageResizeH, err = strconv.Atoi(imageResizeHStr) this.PanicError(err) if imageResizeH < 0 || imageResizeH > 4096 { panic("缩放尺寸不能超过4096") } } return true, imageResizeM, imageResizeW, imageResizeH } else { return false, "", 0, 0 } } //图片预处理功能。 func (this *ImageCacheService) ResizeImage(request *http.Request, filePath string) *image.NRGBA { diskFile, err := os.Open(filePath) this.PanicError(err) defer func() { e := diskFile.Close() this.PanicError(e) }() _, imageResizeM, imageResizeW, imageResizeH := this.ResizeParams(request) //单边缩略 if imageResizeM == "fit" { //将图缩略成宽度为100,高度按比例处理。 if imageResizeW != 0 { src, err := imaging.Decode(diskFile) this.PanicError(err) return imaging.Resize(src, imageResizeW, 0, imaging.Lanczos) } else if imageResizeH != 0 { //将图缩略成高度为100,宽度按比例处理。 src, err := imaging.Decode(diskFile) this.PanicError(err) return imaging.Resize(src, 0, imageResizeH, imaging.Lanczos) } else { panic("单边缩略必须指定宽或者高") } } else if imageResizeM == "fill" { //固定宽高,自动裁剪 if imageResizeW > 0 && imageResizeH > 0 { src, err := imaging.Decode(diskFile) this.PanicError(err) return imaging.Fill(src, imageResizeW, imageResizeH, imaging.Center, imaging.Lanczos) } else { panic("固定宽高,自动裁剪 必须同时指定宽和高") } } else if imageResizeM == "fixed" { //强制宽高缩略 if imageResizeW > 0 && imageResizeH > 0 { src, err := imaging.Decode(diskFile) this.PanicError(err) return imaging.Resize(src, imageResizeW, imageResizeH, imaging.Lanczos) } else { panic("强制宽高缩略必须同时指定宽和高") } } else { panic("不支持" + imageResizeM + "处理模式") } } //缓存一张图片 func (this *ImageCacheService) cacheImage(writer http.ResponseWriter, request *http.Request, matter *Matter) *ImageCache { //当前的文件是否是图片,只有图片才能处理。 extension := util.GetExtension(matter.Name) formats := map[string]imaging.Format{ ".jpg": imaging.JPEG, ".jpeg": imaging.JPEG, ".png": imaging.PNG, ".tif": imaging.TIFF, ".tiff": imaging.TIFF, ".bmp": imaging.BMP, ".gif": imaging.GIF, } _, imageResizeM, imageResizeW, imageResizeH := this.ResizeParams(request) mode := fmt.Sprintf("%s_%d_%d", imageResizeM, imageResizeW, imageResizeH) format, ok := formats[extension] if !ok { panic("该图片格式不支持处理") } user := this.userDao.FindByUuid(matter.UserUuid) //resize图片 dstImage := this.ResizeImage(request, matter.AbsolutePath()) cacheImageName := util.GetSimpleFileName(matter.Name) + "_" + mode + extension cacheImageRelativePath := util.GetSimpleFileName(matter.Path) + "_" + mode + extension cacheImageAbsolutePath := GetUserCacheRootDir(user.Username) + util.GetSimpleFileName(matter.Path) + "_" + mode + extension //创建目录。 dir := filepath.Dir(cacheImageAbsolutePath) util.MakeDirAll(dir) fileWriter, err := os.Create(cacheImageAbsolutePath) this.PanicError(err) defer func() { e := fileWriter.Close() this.PanicError(e) }() //处理后的图片存放在本地 err = imaging.Encode(fileWriter, dstImage, format) this.PanicError(err) //获取新文件的大小 fileInfo, err := fileWriter.Stat() this.PanicError(err) //相关信息写到缓存中去 imageCache := &ImageCache{ Name: cacheImageName, UserUuid: matter.UserUuid, Username: user.Username, MatterUuid: matter.Uuid, MatterName: matter.Name, Mode: mode, Size: fileInfo.Size(), Path: cacheImageRelativePath, } this.imageCacheDao.Create(imageCache) return imageCache }