目錄:git
前序github
效果圖golang
簡介canvas
所有代碼函數
前序:ui
接觸 golang 不久,一直是邊學邊作,邊總結,深深感到這門語言的魅力,等下要跟你們分享是最近項目 服務端 用到的圖片壓縮程序,我單獨分離了出來,作成了 exe 程序,能夠在 Window 下運行。也能夠放到 Linux 環境下編譯運行,golang 是一種靜態、跨平臺的語言。spa
效果圖3d
-
code
壓縮前 壓縮後
orm
開始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