golang golang
package main import ( "encoding/base64" "fmt" "os" "net/http" "io/ioutil" "strings" "path/filepath" "encoding/json" "errors" "bytes" "time" "sync" "strconv" "bufio" "io" ) var sum_file_count int = 0 var handle_file_count int = 0 var wg sync.WaitGroup var invalid_keys map[string]int var keys_user_count map[string]int func main() { url := "https://api.tinify.com/shrink" if len(os.Args) < 2 { fmt.Println("==該工具用於使用 TinyPng 服務進行圖片壓縮處理,須要傳一個文件夾參數,程序會自動遞歸搜索其中的 *.png 文件進行處理,處理完畢後會覆蓋原文件==\n") return } dirPath := os.Args[1] keys := []string{} key_file, err := os.Open("keys.txt") defer key_file.Close() if err != nil { error_msg := fmt.Sprintf("讀取 KEY 文件錯誤,文件路徑錯誤! (path: %s\terror: %s)", "keys.txt", err.Error()) fmt.Printf(error_msg) return } rd := bufio.NewReader(key_file) for { line, err := rd.ReadString('\n') if err != nil || err == io.EOF { break } if len(line) > 1 { keys = append(keys,strings.Trim(line,"\n")) } } /* for _,v := range keys{ println(v) } */ files, err := WalkDir(dirPath, ".png") if err != nil { fmt.Println("請檢查目錄是否存在~~") return } sum_file_count = len(files) if sum_file_count == 0 { fmt.Println("沒有 *.png 圖片須要處理~~") return } fmt.Printf("有 %d 張圖片須要處理,請稍候~~\n",sum_file_count) invalid_keys = make(map[string]int) keys_user_count = make(map[string]int) startTime := time.Now() for i:=0;i<sum_file_count;i++ { wg.Add(1) shrink(url,keys,files[i]) } wg.Wait() fmt.Printf("任務總耗時: %s\n",time.Since(startTime)) if len(keys_user_count) > 0 { fmt.Printf("\n==KEY的本月已使用次數以下:\n") for k,v := range keys_user_count { fmt.Printf("KEY: %s\tUseCount: %d\n",k,v) } } } //上傳圖片,阻塞等待服務器壓縮,服務器壓縮成功返回結果後,去下載圖片覆蓋原圖片 func shrink(url string, keys []string, filePath string) error { defer wg.Done() file_bytes, err := ioutil.ReadFile(filePath) if err != nil { error_msg := fmt.Sprintf("讀取本地文件錯誤,文件路徑錯誤! (path: %s\terror: %s)", filePath, err.Error()) fmt.Println(error_msg) return err } down_url := "" for i:=0;i<len(keys);i++ {if _,ok := invalid_keys[keys[i]];ok { continue } req, err := http.NewRequest("POST", url, bytes.NewReader(file_bytes)) if err != nil { error_msg := fmt.Sprintf("請求 Tiny 出現網絡錯誤! (error: %s)\n",err.Error()) fmt.Println(error_msg) return errors.New(error_msg) } credentials := base64.StdEncoding.EncodeToString([]byte("api:" + keys[i])) req.Header.Set("Authorization", "Basic "+credentials) //處理返回結果 res, err := http.DefaultClient.Do(req) if err != nil { error_msg := fmt.Sprintf("請求 Tiny 出現網絡錯誤! (error: %s)\n",err.Error()) fmt.Println(error_msg) return errors.New(error_msg) } status := res.StatusCode body, err := ioutil.ReadAll(res.Body) if err != nil { error_msg := fmt.Sprintf("請求 Tiny 出現網絡錯誤! (error: %s)\n",err.Error()) fmt.Println(error_msg) return errors.New(error_msg) } //判斷HTTP狀態碼,若是是 415表示文件類型不正確;401表示證書不正確;400表示輸入文件爲空;5xx表示服務器異常;2xx表示成功 if status == 401 || status == 400 { //KEY不正確,使用下一個KEY /* if status == 400 { fmt.Printf("該 KEY: %s 極可能是格式非法,請留意。\n",keys[i]) } */ invalid_keys[keys[i]] = 1 time.Sleep(time.Millisecond * 50) continue } else if status < 300 && status >= 200 { //正確 right_data := new(RightData) json.Unmarshal(body, right_data) down_url = right_data.Output.Url CompressionCount := res.Header.Get("Compression-Count") CompressionCountInt, err := strconv.Atoi(CompressionCount) if err != nil { CompressionCountInt = 0 } if CompressionCountInt > keys_user_count[keys[i]] { keys_user_count[keys[i]] = CompressionCountInt } break } else { //其它錯誤 error_msg := fmt.Sprintf("Tiny狀態碼: %d\tTiny錯誤信息: %s\n",res.StatusCode,string(body)) fmt.Println(error_msg) return errors.New(error_msg) } } if down_url == "" { error_msg := "可能全部 KEY 都已經不可用。" fmt.Println(error_msg) return errors.New(error_msg) } //CompressionCount := res.Header.Get("Compression-Count") //fmt.Printf("Key(%s) UseCount: %s\n",key,CompressionCount) //覆蓋原文件 down_bytes := []byte{} for i:= 0;i < 10;i++ { if i>0 { fmt.Printf("下載圖片 %s 出錯,正在進行第 %d 次嘗試...\n",down_url,i+1) time.Sleep(time.Millisecond*50) } res_down, err := http.Get(down_url) if err != nil { continue } down_bytes, err = ioutil.ReadAll(res_down.Body) if err != nil { continue } break } if len(down_bytes) == 0 { error_msg := fmt.Sprintf("下載圖片出錯!(filePath: %s\turl: %s\terror: %s)\n", filePath,down_url,err.Error()) fmt.Printf(error_msg) return errors.New(error_msg) } file, err := os.OpenFile(filePath,os.O_RDWR | os.O_TRUNC, 0600) defer file.Close() if err != nil { error_msg := fmt.Sprintf("清空原圖片出錯!(filePath: %s\turl: %s\terror: %s)\n", filePath,down_url,err.Error()) fmt.Printf(error_msg) return errors.New(error_msg) } _, err = file.Write(down_bytes) if err != nil { error_msg := fmt.Sprintf("覆寫原圖片出錯!(filePath: %s\turl: %s\terror: %s)\n", filePath,down_url,err.Error()) fmt.Printf(error_msg) return errors.New(error_msg) } handle_file_count += 1 fmt.Printf("任務進度[%d/%d]\n",handle_file_count,sum_file_count) return nil } //獲取指定目錄下的全部文件,不進入下一級目錄搜索,能夠匹配後綴過濾。 func ListDir(dirPth string, suffix string) (files []string, err error) { files = make([]string, 0, 10) dir, err := ioutil.ReadDir(dirPth) if err != nil { return nil, err } PthSep := string(os.PathSeparator) suffix = strings.ToUpper(suffix) //忽略後綴匹配的大小寫 for _, fi := range dir { if fi.IsDir() { // 忽略目錄 continue } if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) { //匹配文件 files = append(files, dirPth+PthSep+fi.Name()) } } return files, nil } //獲取指定目錄及全部子目錄下的全部文件,能夠匹配後綴過濾。 func WalkDir(dirPth, suffix string) (files []string, err error) { files = make([]string, 0, 30) suffix = strings.ToUpper(suffix) //忽略後綴匹配的大小寫 err = filepath.Walk(dirPth, func(filename string, fi os.FileInfo, err error) error { //遍歷目錄 //if err != nil { //忽略錯誤 // return err //} if fi.IsDir() { // 忽略目錄 return nil } if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) { files = append(files, filename) } return nil }) return files, err } type RightData struct { Input struct { Size int `json:"size"` Type string `json:"type"` } `json:"input"` Output struct { Size int `json:"size"` Type string `json:"type"` Width int `json:"width"` Height int `json:"height"` Ratio float32 `json:"ratio"` Url string `json:"url"` } }