当客户端上传一张图片的时候,通常用户不会在意上传文件的大小。而服务端接收文件的时候,首先要保证客户原始文件的正确保存,但是到图片被调用的时候,图片如果过大的话,会占用很大的带宽。
于是有很多场景,要求服务端能够把图片变的小一点。
那么需要有原始图片,有压缩率,有目标大小,还有输出图片。先写个提示
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),会有失败的情况发生。因此,本例是不完美的解决方案。