63. 图片压缩

发布于 2018-04-23 作者 超级苦工 240次 浏览 版块 分享

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

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),会有失败的情况发生。因此,本例是不完美的解决方案。

收藏
暂无回复