目录:
前序
效果图
简介
全部代码
前序:
接触 golang 不久,一直是边学边做,边总结,深深感到这门语言的魅力,等下要跟大家分享是最近项目 服务端 用到的图片压缩程序,我单独分离了出来,做成了 exe 程序,可以在 Window 下运行。也可以放到 Linux 环境下编译运行,golang 是一种静态、跨平台的语言。
效果图
压缩前
压缩后
开始main:
showTips 做了一些有好提示的文字输出,execute 是核心,压缩函数的调用也在里面
1 func main() {
2 showTips()
3 execute()
4 time.Sleep(5 * time.Minute) /** 如果不是自己点击退出,延时5分钟 */
5 }
提示函数
我分离了两种压缩形式,批量和单张,再组合质量和尺寸,压缩100张600K的图片到8~9K,200px宽度,仅用了6秒左右,win 10,12G,i5,ssd。
还可以做完全的,宽和高像素尺寸的限制,只需要改变几个参数,大家先来看看程序运行的时候显示给用户的提示信息:
对于批量压缩,自动遍历用户输入的文件夹里面的所有符合格式的文件,并进行压缩。
1 func showTips() {
2 tips := []string{
3 "请输入文件夹或图片路径:",
4 "如果输入文件夹,那么该目录的图片将会被批量压缩;",
5 "如果是图片路径,那么将会被单独压缩处理。",
6 "例如:",
7 "C:/Users/lzq/Desktop/headImages/ 75 200",
8 "指桌面 headImages 文件夹,里面的图片质量压缩到75%,宽分辨率为200,高是等比例计算",
9 "C:/Users/lzq/Desktop/headImages/1.jpg 75 200",
10 "指桌面的 headImages 文件夹里面的 1.jpg 图片,质量压缩到75%,宽分辨率为200,高是等比例计算 ",
11 "请输入:"}
12 itemLen := len(tips)
13 for i :=0;i<itemLen;i++ {
14 if i == itemLen -1 {
15 fmt.Printf(tips[i])
16 }else{
17 fmt.Println(tips[i])
18 }
19 }
20 }
压缩结构体:
这个比较简单,其余添加可以自定义
1 type InputArgs struct {
2 OutputPath string /** 输出目录 */
3 LocalPath string /** 输入的目录或文件路径 */
4 Quality int /** 质量 */
5 Width int /** 宽度尺寸,像素单位 */
6 }
图片格式验证
自定义支持的文件格式,主要是图片的格式,同时拆分返回一些关键的信息,例如尾缀
1 /** 是否是图片 */
2 func isPictureFormat(path string) (string,string,string) {
3 temp := strings.Split(path,".")
4 if len(temp) <=1 {
5 return "","",""
6 }
7 mapRule := make(map[string]int64)
8 mapRule["jpg"] = 1
9 mapRule["png"] = 1
10 mapRule["jpeg"] = 1
11 // fmt.Println(temp[1]+"---")
12 /** 添加其他格式 */
13 if mapRule[temp[1]] == 1 {
14 println(temp[1])
15 return path,temp[1],temp[0]
16 }else{
17 return "","",""
18 }
19 }
文件夹遍历
主要用于批量压缩,做了所输入的目录的图片文件遍历,和要保存到的文件夹的创建,和采用纳秒级做压缩后的图片的名称。
1 func getFilelist(path string) {
2 /** 创建输出目录 */
3 errC := os.MkdirAll(inputArgs.OutputPath, 0777)
4 if errC != nil {
5 fmt.Printf("%s", errC)
6 return
7 }
8 err := filepath.Walk(path, func(pathFound string, f os.FileInfo, err error) error {
9 if ( f == nil ) {
10 return err
11 }
12 if f.IsDir() { /** 是否是目录 */
13 return nil
14 }
15 // println(pathFound)
16 /** 找到一个文件 */
17 /** 判断是不是图片 */
18 localPath,format,_ := isPictureFormat(pathFound)
19 /** 随机数 */
20 t := time.Now()
21 millis := t.Nanosecond() /** 纳秒 */
22 outputPath := inputArgs.OutputPath+strconv.FormatInt(int64(millis),10)+"."+format
23 if localPath!="" {
24 if !imageCompress(
25 func() (io.Reader,error){
26 return os.Open(localPath)
27 },
28 func() (*os.File,error) {
29 return os.Open(localPath)
30 },
31 outputPath,
32 inputArgs.Quality,
33 inputArgs.Width,format) {
34 fmt.Println("生成缩略图失败")
35 }else{
36 fmt.Println("生成缩略图成功 "+outputPath)
37 }
38 }
39 return nil
40 })
41 if err != nil {
42 fmt.Printf("输入的路径信息有误 %v\n", err)
43 }
44 }
压缩前处理函数:
主要做了压缩结构体数据的配置,和验证用户路径的输入以及最终压缩输出文件目录的路径组合。这里有个坑点,对于控制台的数据获取,最好使用 bufio.NewReader(os.Stdin) 而不是 fmt.Scanf 否则,在fmt.p... 输出错误提示信息的时候也会被当作输入读取了,而不是用户输入的。
func execute() {
/** 获取输入 */
//str := ""
//fmt.Scanln (&str) /** 不要使用 scanf,它不会并以一个新行结束输入 */
reader := bufio.NewReader(os.Stdin)
data, _, _ := reader.ReadLine()
/** 分割 */
strPice := strings.Split(string(data)," ") /** 空格 */
if len(strPice) < 3 {
fmt.Printf("输入有误,参数数量不足,请重新输入或退出程序:")
execute()
return
}
inputArgs.LocalPath = strPice[0]
inputArgs.Quality,_ = strconv.Atoi(strPice[1])
inputArgs.Width,_ = strconv.Atoi(strPice[2])
pathTemp,format,top := isPictureFormat(inputArgs.LocalPath)
if pathTemp == "" {
/** 目录 */
/** 如果输入目录,那么是批量 */
fmt.Println("开始批量压缩...")
rs := []rune(inputArgs.LocalPath)
end := len(rs)
substr := string(rs[end-1:end])
if substr=="/" {
/** 有 / */
rs := []rune(inputArgs.LocalPath)
end := len(rs)
substr := string(rs[0:end-1])
endIndex := strings.LastIndex(substr,"/")
inputArgs.OutputPath = string(rs[0:endIndex])+"/LghImageCompress/";
}else {
endIndex := strings.LastIndex(inputArgs.LocalPath,"/")
inputArgs.OutputPath = string(rs[0:endIndex])+"/LghImageCompress/";
}
getFilelist(inputArgs.LocalPath)
}else{
/** 单个 */
/** 如果输入文件,那么是单个,允许自定义路径 */
fmt.Println("开始单张压缩...")
inputArgs.OutputPath = top+"_compress."+format
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,format) {
fmt.Println("生成缩略图失败")
}else{
fmt.Println("生成缩略图成功 "+inputArgs.OutputPath)
finish()
}
}
}
压缩函数(核心):
基于golang 1.7 自带的 image/jpeg 库。所谓的宽高完全自定义的修改,就在这里,我是采用了等比例缩放,所以只需要传入其中一项。里面分两次读写同一个文件是因为一次用于尺寸读取,而且两次是不能共用的,会出错。
1 func imageCompress(
2 getReadSizeFile func() (io.Reader,error),
3 getDecodeFile func() (*os.File,error),
4 to string,
5 Quality,
6 base int,
7 format string) bool{
8 /** 读取文件 */
9 file_origin, err := getDecodeFile()
10 defer file_origin.Close()
11 if err != nil {
12 fmt.Println("os.Open(file)错误");
13 log.Fatal(err)
14 return false
15 }
16 var origin image.Image
17 var config image.Config
18 var temp io.Reader
19 /** 读取尺寸 */
20 temp, err = getReadSizeFile()
21 if err != nil {
22 fmt.Println("os.Open(temp)");
23 log.Fatal(err)
24 return false
25 }
26 var typeImage int64
27 format = strings.ToLower(format)
28 /** jpg 格式 */
29 if format=="jpg" || format =="jpeg" {
30 typeImage = 1
31 origin, err = jpeg.Decode(file_origin)
32 if err != nil {
33 fmt.Println("jpeg.Decode(file_origin)");
34 log.Fatal(err)
35 return false
36 }
37 temp, err = getReadSizeFile()
38 if err != nil {
39 fmt.Println("os.Open(temp)");
40 log.Fatal(err)
41 return false
42 }
43 config,err = jpeg.DecodeConfig(temp);
44 if err != nil {
45 fmt.Println("jpeg.DecodeConfig(temp)");
46 return false
47 }
48 }else if format=="png" {
49 typeImage = 0
50 origin, err = png.Decode(file_origin)
51 if err != nil {
52 fmt.Println("png.Decode(file_origin)");
53 log.Fatal(err)
54 return false
55 }
56 temp, err = getReadSizeFile()
57 if err != nil {
58 fmt.Println("os.Open(temp)");
59 log.Fatal(err)
60 return false
61 }
62 config,err = png.DecodeConfig(temp);
63 if err != nil {
64 fmt.Println("png.DecodeConfig(temp)");
65 return false
66 }
67 }
68 /** 做等比缩放 */
69 width := uint(base) /** 基准 */
70 height := uint(base*config.Height/config.Width)
71
72 canvas := resize.Thumbnail(width, height, origin, resize.Lanczos3)
73 file_out, err := os.Create(to)
74 defer file_out.Close()
75 if err != nil {
76 log.Fatal(err)
77 return false
78 }
79 if typeImage==0 {
80 err = png.Encode(file_out, canvas)
81 if err!=nil {
82 fmt.Println("压缩图片失败");
83 return false
84 }
85 }else{
86 err = jpeg.Encode(file_out, canvas, &jpeg.Options{Quality})
87 if err!=nil {
88 fmt.Println("压缩图片失败");
89 return false
90 }
91 }
92
93 return true
94 }
全部代码
gitHub: https://github.com/af913337456/golang_image_compress