前序:

  接触 golang 不久,一直是边学边做,边总结,深深感到这门语言的魅力,等下要跟大家分享是最近项目 服务端 用到的图片压缩程序,我单独分离了出来,做成了 exe 程序,可以在 Window 下运行。也可以放到 Linux 环境下编译运行,golang 是一种静态、跨平台的语言。

 

效果图

  移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训  -移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

  移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

压缩前移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训 压缩后移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

 

开始main:

  showTips 做了一些有好提示的文字输出,execute 是核心,压缩函数的调用也在里面

1 func main() {2     showTips()3     execute()4     time.Sleep(5 * time.Minute) /** 如果不是自己点击退出,延时5分钟 */5 }

 

 提示函数

  我分离了两种压缩形式,批量和单张,再组合质量和尺寸,压缩100张600K的图片到8~9K,200px宽度,仅用了6秒左右,win 10,12G,i5,ssd。

  还可以做完全的,宽和高像素尺寸的限制,只需要改变几个参数,大家先来看看程序运行的时候显示给用户的提示信息:

  对于批量压缩,自动遍历用户输入的文件夹里面的所有符合格式的文件,并进行压缩。

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

 1 func showTips()  { 2     tips := []string{ 3         "请输入文件夹或图片路径:", 4         "如果输入文件夹,那么该目录的图片将会被批量压缩;", 5         "如果是图片路径,那么将会被单独压缩处理。", 6         "例如:", 7         "C:/Users/lzq/Desktop/headImages/ 75 200", 8         "指桌面 headImages 文件夹,里面的图片质量压缩到75%,宽分辨率为200,高是等比例计算", 9         "C:/Users/lzq/Desktop/headImages/1.jpg 75 200",10         "指桌面的 headImages 文件夹里面的 1.jpg 图片,质量压缩到75%,宽分辨率为200,高是等比例计算 ",11         "请输入:"}12     itemLen := len(tips)13     for i :=0;i<itemLen;i++ {14         if i == itemLen -1 {15             fmt.Printf(tips[i])16         }else{17             fmt.Println(tips[i])18         }19     }20 }

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

 

压缩结构体:

  这个比较简单,其余添加可以自定义

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

1 type InputArgs struct {2     OutputPath string  /** 输出目录 */3     LocalPath  string  /** 输入的目录或文件路径 */4     Quality    int     /** 质量 */5     Width      int     /** 宽度尺寸,像素单位 */6 }

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

 

图片格式验证

  自定义支持的文件格式,主要是图片的格式,同时拆分返回一些关键的信息,例如尾缀

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

 1 /** 是否是图片 */ 2 func isPictureFormat(path string) (string,string,string) { 3     temp := strings.Split(path,".") 4     if len(temp) <=1 { 5         return "","","" 6     } 7     mapRule := make(map[string]int64) 8     mapRule["jpg"]  = 1 9     mapRule["png"]  = 110     mapRule["jpeg"] = 111     // fmt.Println(temp[1]+"---")12     /** 添加其他格式 */13     if mapRule[temp[1]] == 1  {14         println(temp[1])15         return path,temp[1],temp[0]16     }else{17         return "","",""18     }19 }

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

 

文件夹遍历

  主要用于批量压缩,做了所输入的目录的图片文件遍历,和要保存到的文件夹的创建,和采用纳秒级做压缩后的图片的名称。

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

 1 func getFilelist(path string) { 2     /** 创建输出目录 */ 3     errC := os.MkdirAll(inputArgs.OutputPath, 0777) 4     if errC != nil { 5         fmt.Printf("%s", errC) 6         return 7     } 8     err := filepath.Walk(path, func(pathFound string, f os.FileInfo, err error) error { 9         if ( f == nil ) {10             return err11         }12         if f.IsDir() { /** 是否是目录 */13             return nil14         }15         // println(pathFound)16         /** 找到一个文件 */17         /** 判断是不是图片 */18         localPath,format,_ := isPictureFormat(pathFound)19         /** 随机数 */20         t := time.Now()21         millis := t.Nanosecond() /** 纳秒 */22         outputPath := inputArgs.OutputPath+strconv.FormatInt(int64(millis),10)+"."+format23         if localPath!="" {24             if !imageCompress(25                 func() (io.Reader,error){26                     return os.Open(localPath)27                 },28                 func() (*os.File,error) {29                     return os.Open(localPath)30                 },31                 outputPath,32                 inputArgs.Quality,33                 inputArgs.Width,format) {34                 fmt.Println("生成缩略图失败")35             }else{36                 fmt.Println("生成缩略图成功 "+outputPath)37             }38         }39         return nil40     })41     if err != nil {42         fmt.Printf("输入的路径信息有误 %v\n", err)43     }44 }

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

 

压缩前处理函数:

  主要做了压缩结构体数据的配置,和验证用户路径的输入以及最终压缩输出文件目录的路径组合。这里有个坑点,对于控制台的数据获取,最好使用 bufio.NewReader(os.Stdin) 而不是 fmt.Scanf 否则,在fmt.p... 输出错误提示信息的时候也会被当作输入读取了,而不是用户输入的。

 

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

func execute()  {    /** 获取输入 */
    //str := ""    //fmt.Scanln (&str) /** 不要使用 scanf,它不会并以一个新行结束输入 */
    reader := bufio.NewReader(os.Stdin)
    data, _, _ := reader.ReadLine()    /** 分割 */
    strPice := strings.Split(string(data)," ") /** 空格 */
    if len(strPice) < 3 {
        fmt.Printf("输入有误,参数数量不足,请重新输入或退出程序:")
        execute()        return
    }

    inputArgs.LocalPath = strPice[0]
    inputArgs.Quality,_ = strconv.Atoi(strPice[1])
    inputArgs.Width,_   = strconv.Atoi(strPice[2])

    pathTemp,format,top := isPictureFormat(inputArgs.LocalPath)    if pathTemp == "" {        /** 目录 */
        /** 如果输入目录,那么是批量 */
        fmt.Println("开始批量压缩...")
        rs := []rune(inputArgs.LocalPath)
        end := len(rs)
        substr := string(rs[end-1:end])        if substr=="/" {            /** 有 / */
            rs := []rune(inputArgs.LocalPath)
            end := len(rs)
            substr := string(rs[0:end-1])
            endIndex := strings.LastIndex(substr,"/")
            inputArgs.OutputPath = string(rs[0:endIndex])+"/LghImageCompress/";
        }else {
            endIndex := strings.LastIndex(inputArgs.LocalPath,"/")
            inputArgs.OutputPath = string(rs[0:endIndex])+"/LghImageCompress/";
        }
        getFilelist(inputArgs.LocalPath)
    }else{        /** 单个 */
        /** 如果输入文件,那么是单个,允许自定义路径 */
        fmt.Println("开始单张压缩...")
        inputArgs.OutputPath = top+"_compress."+format        if !imageCompress(
            func() (io.Reader,error){                return os.Open(inputArgs.LocalPath)
            },
            func() (*os.File,error) {                return os.Open(inputArgs.LocalPath)
            },
            inputArgs.OutputPath,
            inputArgs.Quality,
            inputArgs.Width,format) {
            fmt.Println("生成缩略图失败")
        }else{
            fmt.Println("生成缩略图成功 "+inputArgs.OutputPath)
            finish()
        }
    }}

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

 

压缩函数(核心):

  基于golang 1.7 自带的 image/jpeg 库。所谓的宽高完全自定义的修改,就在这里,我是采用了等比例缩放,所以只需要传入其中一项。里面分两次读写同一个文件是因为一次用于尺寸读取,而且两次是不能共用的,会出错。

 

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

 1 func imageCompress( 2     getReadSizeFile func() (io.Reader,error), 3     getDecodeFile func() (*os.File,error), 4     to string, 5     Quality, 6     base int, 7     format string) bool{ 8     /** 读取文件 */ 9     file_origin, err := getDecodeFile()10     defer file_origin.Close()11     if err != nil {12         fmt.Println("os.Open(file)错误");13         log.Fatal(err)14         return false15     }16     var origin image.Image17     var config image.Config18     var temp io.Reader19     /** 读取尺寸 */20     temp, err = getReadSizeFile()21     if err != nil {22         fmt.Println("os.Open(temp)");23         log.Fatal(err)24         return false25     }26     var typeImage int6427     format = strings.ToLower(format)28     /** jpg 格式 */29     if format=="jpg" || format =="jpeg" {30         typeImage = 131         origin, err = jpeg.Decode(file_origin)32         if err != nil {33             fmt.Println("jpeg.Decode(file_origin)");34             log.Fatal(err)35             return false36         }37         temp, err = getReadSizeFile()38         if err != nil {39             fmt.Println("os.Open(temp)");40             log.Fatal(err)41             return false42         }43         config,err = jpeg.DecodeConfig(temp);44         if err != nil {45             fmt.Println("jpeg.DecodeConfig(temp)");46             return false47         }48     }else if format=="png" {49         typeImage = 050         origin, err = png.Decode(file_origin)51         if err != nil {52             fmt.Println("png.Decode(file_origin)");53             log.Fatal(err)54             return false55         }56         temp, err = getReadSizeFile()57         if err != nil {58             fmt.Println("os.Open(temp)");59             log.Fatal(err)60             return false61         }62         config,err = png.DecodeConfig(temp);63         if err != nil {64             fmt.Println("png.DecodeConfig(temp)");65             return false66         }67     }68     /** 做等比缩放 */69     width  := uint(base) /** 基准 */70     height := uint(base*config.Height/config.Width)71 72     canvas := resize.Thumbnail(width, height, origin, resize.Lanczos3)73     file_out, err := os.Create(to)74     defer file_out.Close()75     if err != nil {76         log.Fatal(err)77         return false78     }79     if typeImage==0 {80         err = png.Encode(file_out, canvas)81         if err!=nil {82             fmt.Println("压缩图片失败");83             return false84         }85     }else{86         err = jpeg.Encode(file_out, canvas, &jpeg.Options{Quality})87         if err!=nil {88             fmt.Println("压缩图片失败");89             return false90         }91     }92 93     return true94 }

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

 

 


网友评论