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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容