63. 圖片壓縮

當(dāng)客戶端上傳一張圖片的時(shí)候,通常用戶不會(huì)在意上傳文件的大小。而服務(wù)端接收文件的時(shí)候,首先要保證客戶原始文件的正確保存,但是到圖片被調(diào)用的時(shí)候,圖片如果過大的話,會(huì)占用很大的帶寬。
于是有很多場景,要求服務(wù)端能夠把圖片變的小一點(diǎn)。
那么需要有原始圖片,有壓縮率,有目標(biāo)大小,還有輸出圖片。先寫個(gè)提示

tips := []string{
        "輸入?yún)?shù):",
    "原始圖片路徑  輸出圖片壓縮率  輸出圖片分辨率寬度  輸出圖片路徑"}
    tipsLen := len(tips)
    for i := 0; i < tipsLen; i++ {
        fmt.Println(tips[i])
    }

這些語句可以輸出一個(gè)提示信息。


提示信息

然后我們需要能夠輸入指令,并對(duì)參數(shù)進(jìn)行分解。

    //--獲取命令參數(shù)
    reader := bufio.NewReader(os.Stdin)     //獲取輸入指令
    data, _, err := reader.ReadLine()       //獲取指令行信息
    if err != nil {
        fmt.Println(err)
    }else {
        strPice := strings.Split(string(data), " ")     //以空格為分界,分解指令參數(shù)
        if len(strPice) < 4 {
            //--參數(shù)不足的時(shí)候執(zhí)行(提示,并重新等待輸入指令)
            fmt.Println("輸入?yún)?shù)個(gè)數(shù)不足。需要四個(gè)參數(shù):原始圖片路徑  輸出圖片壓縮率  輸出圖片分辨率寬度  輸出圖片路徑")
            play()
            return
        }else {
            //--參數(shù)充足,分解參數(shù)并初始化變量
            inputArgs.LocalPath = strPice[0]
            inputArgs.Quality, err = strconv.Atoi(strPice[1])
            if err != nil {
                inputArgs.Quality = 100     //默認(rèn)不壓縮
            }
            inputArgs.Width, err = strconv.Atoi(strPice[2])
            if err != nil {
                inputArgs.Width = 48        //默認(rèn)常見ico圖標(biāo)寬度
            }
            inputArgs.OutputPath = strPice[3]

inputArgs是參數(shù)的結(jié)構(gòu),這里可以定義一個(gè)結(jié)構(gòu)體,再聲明一個(gè)變量。

/**參數(shù)結(jié)構(gòu)體*/
type InputArgs struct {
    OutputPath string /** 輸出目錄 */
    LocalPath  string /** 輸入的目錄或文件路徑 */
    Quality    int    /** 質(zhì)量 */
    Width      int    /** 寬度尺寸,像素單位 */
    Format     string /** 格式 */
}

var inputArgs InputArgs

顯然還需要一個(gè)必要的判斷,檢查輸入的文件路徑是否為圖片路徑。

/**是否是圖片,返回值: 路徑,圖片格式,圖片名*/
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 "", "", ""
    }
}

這個(gè)函數(shù),如果確定了是一個(gè)圖片,就返回圖片的相關(guān)信息。路徑、圖片格式(圖片文件名后綴)、圖片文件名稱。如果不是圖片,就返回空。
對(duì)各種格式的圖片,采用相應(yīng)的 Encode 方法。改變大小的操作,就沒有重復(fù)造輪子了。引用"github.com/nfnt/resize"
編寫imageCompress函數(shù)進(jìn)行分別操作。

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)錯(cuò)誤");
        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) /** 基準(zhǔn) */
    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"
)

/**打印當(dāng)前運(yùn)行*/
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{
        "輸入?yún)?shù):",
        "原始圖片路徑  輸出圖片壓縮率  輸出圖片分辨率寬度  輸出圖片路徑"}
    tipsLen := len(tips)
    for i := 0; i < tipsLen; i++ {
        fmt.Println(tips[i])
    }
    play()
}

/**執(zhí)行*/
func play() {
    //--獲取命令參數(shù)
    reader := bufio.NewReader(os.Stdin) //獲取輸入指令
    data, _, err := reader.ReadLine()   //獲取指令行信息
    if err != nil {
        fmt.Println(err)
    } else {
        strPice := strings.Split(string(data), " ") //以空格為分界,分解指令參數(shù)
        if len(strPice) < 4 {
            //--參數(shù)不足的時(shí)候執(zhí)行(提示,并重新等待輸入指令)
            fmt.Println("輸入?yún)?shù)個(gè)數(shù)不足。需要四個(gè)參數(shù):原始圖片路徑  輸出圖片壓縮率  輸出圖片分辨率寬度  輸出圖片路徑")
            play()
            return
        } else {
            //--參數(shù)充足,分解參數(shù)并初始化變量
            inputArgs.LocalPath = strPice[0]
            inputArgs.Quality, err = strconv.Atoi(strPice[1])
            if err != nil {
                inputArgs.Quality = 100 //默認(rèn)不壓縮
            }
            inputArgs.Width, err = strconv.Atoi(strPice[2])
            if err != nil {
                inputArgs.Width = 48 //默認(rèn)常見ico圖標(biāo)寬度
            }
            inputArgs.OutputPath = strPice[3]

            pathTemp, picType, picName := isPic(inputArgs.LocalPath)

            if pathTemp == "" {
                //--當(dāng)前給出的不是一個(gè)圖片
                log.Fatal("這不是圖片")
            } else {
                //--當(dāng)前給出的是一個(gè)圖片
                //--設(shè)置當(dāng)前輸出
                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 "", "", ""
    }
}

/**參數(shù)結(jié)構(gòu)體*/
type InputArgs struct {
    OutputPath string /** 輸出目錄 */
    LocalPath  string /** 輸入的目錄或文件路徑 */
    Quality    int    /** 質(zhì)量 */
    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)錯(cuò)誤");
        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) /** 基準(zhǔn) */
    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
}

調(diào)用代碼

joelImageCompress.Compress()

運(yùn)行指令

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

我在 e 盤的根上放置了一張 quan.jpg 圖片。就針對(duì)它操作了。


運(yùn)行結(jié)果

圖片效果

運(yùn)行結(jié)果

圖片效果

經(jīng)過測(cè)試,如果圖片的后綴是被修改過的(例如jpeg改成png),會(huì)有失敗的情況發(fā)生。因此,本例是不完美的解決方案。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容