63. 图片压缩

作者: 厚土火焱 | 来源:发表于2018-04-15 01:29 被阅读63次

    当客户端上传一张图片的时候,通常用户不会在意上传文件的大小。而服务端接收文件的时候,首先要保证客户原始文件的正确保存,但是到图片被调用的时候,图片如果过大的话,会占用很大的带宽。
    于是有很多场景,要求服务端能够把图片变的小一点。
    那么需要有原始图片,有压缩率,有目标大小,还有输出图片。先写个提示

    tips := []string{
            "输入参数:",
        "原始图片路径  输出图片压缩率  输出图片分辨率宽度  输出图片路径"}
        tipsLen := len(tips)
        for i := 0; i < tipsLen; i++ {
            fmt.Println(tips[i])
        }
    

    这些语句可以输出一个提示信息。


    提示信息

    然后我们需要能够输入指令,并对参数进行分解。

        //--获取命令参数
        reader := bufio.NewReader(os.Stdin)     //获取输入指令
        data, _, err := reader.ReadLine()       //获取指令行信息
        if err != nil {
            fmt.Println(err)
        }else {
            strPice := strings.Split(string(data), " ")     //以空格为分界,分解指令参数
            if len(strPice) < 4 {
                //--参数不足的时候执行(提示,并重新等待输入指令)
                fmt.Println("输入参数个数不足。需要四个参数:原始图片路径  输出图片压缩率  输出图片分辨率宽度  输出图片路径")
                play()
                return
            }else {
                //--参数充足,分解参数并初始化变量
                inputArgs.LocalPath = strPice[0]
                inputArgs.Quality, err = strconv.Atoi(strPice[1])
                if err != nil {
                    inputArgs.Quality = 100     //默认不压缩
                }
                inputArgs.Width, err = strconv.Atoi(strPice[2])
                if err != nil {
                    inputArgs.Width = 48        //默认常见ico图标宽度
                }
                inputArgs.OutputPath = strPice[3]
    

    inputArgs是参数的结构,这里可以定义一个结构体,再声明一个变量。

    /**参数结构体*/
    type InputArgs struct {
        OutputPath string /** 输出目录 */
        LocalPath  string /** 输入的目录或文件路径 */
        Quality    int    /** 质量 */
        Width      int    /** 宽度尺寸,像素单位 */
        Format     string /** 格式 */
    }
    
    var inputArgs InputArgs
    

    显然还需要一个必要的判断,检查输入的文件路径是否为图片路径。

    /**是否是图片,返回值: 路径,图片格式,图片名*/
    func isPic(path string) (string, string, string) {
        temp := strings.Split(path, ".")
        if len(temp) <= 1 {
            return "", "", ""
        }
        mapPicType := make(map[string]int64)
        mapPicType["jpg"] = 1
        mapPicType["jpeg"] = 1
        mapPicType["png"] = 1
        mapPicType["gif"] = 1
        mapPicType["bmp"] = 1
    
        if mapPicType[temp[1]] == 1 {
            fmt.Println(temp[1])
            return path, temp[1], temp[0]
        } else {
            return "", "", ""
        }
    }
    

    这个函数,如果确定了是一个图片,就返回图片的相关信息。路径、图片格式(图片文件名后缀)、图片文件名称。如果不是图片,就返回空。
    对各种格式的图片,采用相应的 Encode 方法。改变大小的操作,就没有重复造轮子了。引用"github.com/nfnt/resize"
    编写imageCompress函数进行分别操作。

    func imageCompress(
        getReadSizeFile func() (io.Reader, error),
        getDecodeFile func() (*os.File, error),
        to string,
        Quality,
        base int,
        format string) bool {
        /** 读取文件 */
        file_origin, err := getDecodeFile()
        defer file_origin.Close()
        if err != nil {
            fmt.Println("os.Open(file)错误");
            log.Fatal(err)
            return false
        }
        var origin image.Image
        var config image.Config
        var temp io.Reader
        /** 读取尺寸 */
        temp, err = getReadSizeFile()
        if err != nil {
            fmt.Println("os.Open(temp)");
            log.Fatal(err)
            return false
        }
        var typeImage int64
        format = strings.ToLower(format)
        /** jpg 格式 */
        if format == "jpg" || format == "jpeg" {
            typeImage = 0
            origin, err = jpeg.Decode(file_origin)
            if err != nil {
                fmt.Println("jpeg.Decode(file_origin)");
                log.Fatal(err)
                return false
            }
            temp, err = getReadSizeFile()
            if err != nil {
                fmt.Println("os.Open(temp)");
                log.Fatal(err)
                return false
            }
            config, err = jpeg.DecodeConfig(temp);
            if err != nil {
                fmt.Println("jpeg.DecodeConfig(temp)");
                return false
            }
        } else if format == "png" {
            typeImage = 1
            origin, err = png.Decode(file_origin)
            if err != nil {
                fmt.Println("png.Decode(file_origin)");
                log.Fatal(err)
                return false
            }
            temp, err = getReadSizeFile()
            if err != nil {
                fmt.Println("os.Open(temp)");
                log.Fatal(err)
                return false
            }
            config, err = png.DecodeConfig(temp);
            if err != nil {
                fmt.Println("png.DecodeConfig(temp)");
                return false
            }
        } else if format == "gif" {
            typeImage = 2
            origin, err = gif.Decode(file_origin)
            if err != nil {
                fmt.Println("gif.Decode(file_origin)");
                log.Fatal(err)
                return false
            }
            temp, err = getReadSizeFile()
            if err != nil {
                fmt.Println("os.Open(temp)");
                log.Fatal(err)
                return false
            }
            config, err = gif.DecodeConfig(temp);
            if err != nil {
                fmt.Println("gif.DecodeConfig(temp)");
                return false
            }
        } else if format == "bmp" {
            typeImage = 3
            origin, err = bmp.Decode(file_origin)
            if err != nil {
                fmt.Println("bmp.Decode(file_origin)");
                log.Fatal(err)
                return false
            }
            temp, err = getReadSizeFile()
            if err != nil {
                fmt.Println("os.Open(temp)");
                log.Fatal(err)
                return false
            }
            config, err = bmp.DecodeConfig(temp);
            if err != nil {
                fmt.Println("bmp.DecodeConfig(temp)");
                return false
            }
        }
        /** 做等比缩放 */
        width := uint(base) /** 基准 */
        height := uint(base * config.Height / config.Width)
    
        canvas := resize.Thumbnail(width, height, origin, resize.Lanczos3)
        file_out, err := os.Create(to)
        defer file_out.Close()
        if err != nil {
            log.Fatal(err)
            return false
        }
        if typeImage == 0 {
            err = jpeg.Encode(file_out, canvas, &jpeg.Options{Quality})
        } else if typeImage == 1 {
            err = png.Encode(file_out, canvas)
        } else if typeImage == 2{
            //--需要再研究
            err = gif.Encode(file_out, canvas, &gif.Options{Quality, nil, nil})
        }else if typeImage == 3{
            err = bmp.Encode(file_out, canvas)
        }else {
            err = png.Encode(file_out, canvas)
        }
    
        if err != nil {
            fmt.Println("压缩图片失败");
            return false
        }
    
        return true
    }
    

    其中 gif 格式还不完美。
    完整代码

    /**
    * CofoxS
    * @Author:  Jian Junbo
    * @Email:   junbojian@qq.com
    * @Create:  2018/4/14 20:22
    * Copyright (c) 2018 Jian Junbo All rights reserved.
    *
    * Description:  
    */
    package joelImageCompress
    
    import (
        "fmt"
        "runtime"
        "bufio"
        "os"
        "strings"
        "strconv"
        "io"
        "log"
        "image"
        "image/jpeg"
        "image/png"
        "image/gif"
        "github.com/nfnt/resize"
        "golang.org/x/image/bmp"
    )
    
    /**打印当前运行*/
    func Ggg() {
        for i := 0; i < 3; i++ {
            pc, file, line, ok := runtime.Caller(i)
            f := runtime.FuncForPC(pc)
            fmt.Println("This is 'runtime.Caller(", i, ")'", pc, ok, line, f.Name(), file)
        }
    }
    
    /**压缩图片_开始*/
    func Compress() {
        tips := []string{
            "输入参数:",
            "原始图片路径  输出图片压缩率  输出图片分辨率宽度  输出图片路径"}
        tipsLen := len(tips)
        for i := 0; i < tipsLen; i++ {
            fmt.Println(tips[i])
        }
        play()
    }
    
    /**执行*/
    func play() {
        //--获取命令参数
        reader := bufio.NewReader(os.Stdin) //获取输入指令
        data, _, err := reader.ReadLine()   //获取指令行信息
        if err != nil {
            fmt.Println(err)
        } else {
            strPice := strings.Split(string(data), " ") //以空格为分界,分解指令参数
            if len(strPice) < 4 {
                //--参数不足的时候执行(提示,并重新等待输入指令)
                fmt.Println("输入参数个数不足。需要四个参数:原始图片路径  输出图片压缩率  输出图片分辨率宽度  输出图片路径")
                play()
                return
            } else {
                //--参数充足,分解参数并初始化变量
                inputArgs.LocalPath = strPice[0]
                inputArgs.Quality, err = strconv.Atoi(strPice[1])
                if err != nil {
                    inputArgs.Quality = 100 //默认不压缩
                }
                inputArgs.Width, err = strconv.Atoi(strPice[2])
                if err != nil {
                    inputArgs.Width = 48 //默认常见ico图标宽度
                }
                inputArgs.OutputPath = strPice[3]
    
                pathTemp, picType, picName := isPic(inputArgs.LocalPath)
    
                if pathTemp == "" {
                    //--当前给出的不是一个图片
                    log.Fatal("这不是图片")
                } else {
                    //--当前给出的是一个图片
                    //--设置当前输出
                    if len(inputArgs.OutputPath) < 1 {
                        inputArgs.OutputPath = picName + "_" + strconv.Itoa(inputArgs.Width) + "." + picType
                    }
                    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,
                        picType) {
                        fmt.Println("生成缩略图失败")
                    } else {
                        fmt.Println("生成缩略图成功")
                    }
                    Compress()
                }
            }
        }
    
    }
    
    /**是否是图片,返回值: 路径,图片格式,图片名*/
    func isPic(path string) (string, string, string) {
        temp := strings.Split(path, ".")
        if len(temp) <= 1 {
            return "", "", ""
        }
        mapPicType := make(map[string]int64)
        mapPicType["jpg"] = 1
        mapPicType["jpeg"] = 1
        mapPicType["png"] = 1
        mapPicType["gif"] = 1
        mapPicType["bmp"] = 1
    
        if mapPicType[temp[1]] == 1 {
            fmt.Println(temp[1])
            return path, temp[1], temp[0]
        } else {
            return "", "", ""
        }
    }
    
    /**参数结构体*/
    type InputArgs struct {
        OutputPath string /** 输出目录 */
        LocalPath  string /** 输入的目录或文件路径 */
        Quality    int    /** 质量 */
        Width      int    /** 宽度尺寸,像素单位 */
        Format     string /** 格式 */
    }
    
    var inputArgs InputArgs
    
    func imageCompress(
        getReadSizeFile func() (io.Reader, error),
        getDecodeFile func() (*os.File, error),
        to string,
        Quality,
        base int,
        format string) bool {
        /** 读取文件 */
        file_origin, err := getDecodeFile()
        defer file_origin.Close()
        if err != nil {
            fmt.Println("os.Open(file)错误");
            log.Fatal(err)
            return false
        }
        var origin image.Image
        var config image.Config
        var temp io.Reader
        /** 读取尺寸 */
        temp, err = getReadSizeFile()
        if err != nil {
            fmt.Println("os.Open(temp)");
            log.Fatal(err)
            return false
        }
        var typeImage int64
        format = strings.ToLower(format)
        /** jpg 格式 */
        if format == "jpg" || format == "jpeg" {
            typeImage = 0
            origin, err = jpeg.Decode(file_origin)
            if err != nil {
                fmt.Println("jpeg.Decode(file_origin)");
                log.Fatal(err)
                return false
            }
            temp, err = getReadSizeFile()
            if err != nil {
                fmt.Println("os.Open(temp)");
                log.Fatal(err)
                return false
            }
            config, err = jpeg.DecodeConfig(temp);
            if err != nil {
                fmt.Println("jpeg.DecodeConfig(temp)");
                return false
            }
        } else if format == "png" {
            typeImage = 1
            origin, err = png.Decode(file_origin)
            if err != nil {
                fmt.Println("png.Decode(file_origin)");
                log.Fatal(err)
                return false
            }
            temp, err = getReadSizeFile()
            if err != nil {
                fmt.Println("os.Open(temp)");
                log.Fatal(err)
                return false
            }
            config, err = png.DecodeConfig(temp);
            if err != nil {
                fmt.Println("png.DecodeConfig(temp)");
                return false
            }
        } else if format == "gif" {
            typeImage = 2
            origin, err = gif.Decode(file_origin)
            if err != nil {
                fmt.Println("gif.Decode(file_origin)");
                log.Fatal(err)
                return false
            }
            temp, err = getReadSizeFile()
            if err != nil {
                fmt.Println("os.Open(temp)");
                log.Fatal(err)
                return false
            }
            config, err = gif.DecodeConfig(temp);
            if err != nil {
                fmt.Println("gif.DecodeConfig(temp)");
                return false
            }
        } else if format == "bmp" {
            typeImage = 3
            origin, err = bmp.Decode(file_origin)
            if err != nil {
                fmt.Println("bmp.Decode(file_origin)");
                log.Fatal(err)
                return false
            }
            temp, err = getReadSizeFile()
            if err != nil {
                fmt.Println("os.Open(temp)");
                log.Fatal(err)
                return false
            }
            config, err = bmp.DecodeConfig(temp);
            if err != nil {
                fmt.Println("bmp.DecodeConfig(temp)");
                return false
            }
        }
        /** 做等比缩放 */
        width := uint(base) /** 基准 */
        height := uint(base * config.Height / config.Width)
    
        canvas := resize.Thumbnail(width, height, origin, resize.Lanczos3)
        file_out, err := os.Create(to)
        defer file_out.Close()
        if err != nil {
            log.Fatal(err)
            return false
        }
        if typeImage == 0 {
            err = jpeg.Encode(file_out, canvas, &jpeg.Options{Quality})
        } else if typeImage == 1 {
            err = png.Encode(file_out, canvas)
        } else if typeImage == 2{
            //--需要再研究
            err = gif.Encode(file_out, canvas, &gif.Options{Quality, nil, nil})
        }else if typeImage == 3{
            err = bmp.Encode(file_out, canvas)
        }else {
            err = png.Encode(file_out, canvas)
        }
    
        if err != nil {
            fmt.Println("压缩图片失败");
            return false
        }
    
        return true
    }
    
    

    调用代码

    joelImageCompress.Compress()
    

    运行指令

    e:\quan.jpg 75 300 e:\quan_ok.jpg
    

    我在 e 盘的根上放置了一张 quan.jpg 图片。就针对它操作了。


    运行结果
    图片效果
    运行结果
    图片效果

    经过测试,如果图片的后缀是被修改过的(例如jpeg改成png),会有失败的情况发生。因此,本例是不完美的解决方案。

    相关文章

      网友评论

        本文标题:63. 图片压缩

        本文链接:https://www.haomeiwen.com/subject/dsohkftx.html