zoukankan      html  css  js  c++  java
  • Golang 编写的图片压缩程序,质量、尺寸压缩,批量、单张压缩

    目录:

      前序

      效果图

      简介

      全部代码

    前序:

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

    效果图

        -

      

    压缩前 压缩后

    开始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。

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

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

     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 }

    压缩结构体:

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

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

    图片格式验证

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

     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"]  = 1
    10     mapRule["jpeg"] = 1
    11     // 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 }

    文件夹遍历

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

     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 err
    11         }
    12         if f.IsDir() { /** 是否是目录 */
    13             return nil
    14         }
    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)+"."+format
    23         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 nil
    40     })
    41     if err != nil {
    42         fmt.Printf("输入的路径信息有误 %v
    ", err)
    43     }
    44 }

    压缩前处理函数:

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

    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()
            }
        }
    }

    压缩函数(核心):

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

     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 false
    15     }
    16     var origin image.Image
    17     var config image.Config
    18     var temp io.Reader
    19     /** 读取尺寸 */
    20     temp, err = getReadSizeFile()
    21     if err != nil {
    22         fmt.Println("os.Open(temp)");
    23         log.Fatal(err)
    24         return false
    25     }
    26     var typeImage int64
    27     format = strings.ToLower(format)
    28     /** jpg 格式 */
    29     if format=="jpg" || format =="jpeg" {
    30         typeImage = 1
    31         origin, err = jpeg.Decode(file_origin)
    32         if err != nil {
    33             fmt.Println("jpeg.Decode(file_origin)");
    34             log.Fatal(err)
    35             return false
    36         }
    37         temp, err = getReadSizeFile()
    38         if err != nil {
    39             fmt.Println("os.Open(temp)");
    40             log.Fatal(err)
    41             return false
    42         }
    43         config,err = jpeg.DecodeConfig(temp);
    44         if err != nil {
    45             fmt.Println("jpeg.DecodeConfig(temp)");
    46             return false
    47         }
    48     }else if format=="png" {
    49         typeImage = 0
    50         origin, err = png.Decode(file_origin)
    51         if err != nil {
    52             fmt.Println("png.Decode(file_origin)");
    53             log.Fatal(err)
    54             return false
    55         }
    56         temp, err = getReadSizeFile()
    57         if err != nil {
    58             fmt.Println("os.Open(temp)");
    59             log.Fatal(err)
    60             return false
    61         }
    62         config,err = png.DecodeConfig(temp);
    63         if err != nil {
    64             fmt.Println("png.DecodeConfig(temp)");
    65             return false
    66         }
    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 false
    78     }
    79     if typeImage==0 {
    80         err = png.Encode(file_out, canvas)
    81         if err!=nil {
    82             fmt.Println("压缩图片失败");
    83             return false
    84         }
    85     }else{
    86         err = jpeg.Encode(file_out, canvas, &jpeg.Options{Quality})
    87         if err!=nil {
    88             fmt.Println("压缩图片失败");
    89             return false
    90         }
    91     }
    92 
    93     return true
    94 }

    全部代码

      gitHub: https://github.com/af913337456/golang_image_compress

  • 相关阅读:
    Kubernetes的RBAC是啥
    《高效学习的七个习惯》总结
    Kubernetes的CronJob对象
    Kubernetes的Job对象
    什么是DaemonSet
    Docker容器日志路径
    什么是StatefulSet
    Kubernetes的Deployment对象使用
    MXNET:卷积神经网络基础
    MXNET:深度学习计算-GPU
  • 原文地址:https://www.cnblogs.com/linguanh/p/6233013.html
Copyright © 2011-2022 走看看