[TOC]html
嗨,我是小魔童哪吒,我們上次分享的GO 中 defer
的實現原理,再來回顧一下吧git
defer
的 3 條規則要是對 GO 中 defer
實現原理還有點興趣的話,歡迎查看文章 GO 中 defer的實現原理github
今天咱們來分享一些使用 GO
實現小案例,我們邊玩邊成長shell
咱們平時使用到的驗證碼大體分爲這幾種,我們梳理一下:數據結構
輸入圖片上的數字,文字,字母等等dom
這個主要是來打廣告的函數
例如,按照提示滑動等等post
例如我們買火車票的時候驗證碼,各類圖標讓你選學習
例如某寶的驗證碼編碼
例如,點觸智能驗證碼
咱們今天就來玩一玩第一種,使用最多的一種驗證碼吧
會使用 GO 的這個驗證碼庫來完成,github.com/dchest/captcha
若咱們向C/C++
同樣,將不少的底層處理都是咱們本身來封裝來實現的話,那仍是挺累人的,GO 這一點確實蠻好,有不少使用的包,我們在使用之餘,也能夠站在巨人的肩膀,學習源碼中的實現方式,學習大佬們的設計思想。
captcha
庫你們使用以下命令就能夠下載下來使用
go get github.com/dchest/captcha
當咱們在GOLAND
中用到 captcha
庫的時候,我們能夠看看源碼目錄
這個庫目前支持的音頻有 4 種語言:
庫中驗證碼的大小默認是 寬 240 px,高 80 px
在源碼中的 image.go
const ( // Standard width and height of a captcha image. StdWidth = 240 StdHeight = 80 // Maximum absolute skew factor of a single digit. maxSkew = 0.7 // Number of background circles. circleCount = 20 ) type Image struct { *image.Paletted numWidth int numHeight int dotSize int rng siprng }
以下是驗證碼id中容許的字符 ,能夠在源碼中看到
源碼包中的 random.go
// idChars are characters allowed in captcha id. var idChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
在源碼的 sounds.go
文件
目前對於音頻只支持 4 種語言,"en", "ja", "ru", "zh".
/ NewAudio returns a new audio captcha with the given digits, where each digit // must be in range 0-9. Digits are pronounced in the given language. If there // are no sounds for the given language, English is used. // // Possible values for lang are "en", "ja", "ru", "zh". func NewAudio(id string, digits []byte, lang string) *Audio { a := new(Audio) // Initialize PRNG. a.rng.Seed(deriveSeed(audioSeedPurpose, id, digits)) if sounds, ok := digitSounds[lang]; ok { a.digitSounds = sounds } else { a.digitSounds = digitSounds["en"] } numsnd := make([][]byte, len(digits)) nsdur := 0 for i, n := range digits { snd := a.randomizedDigitSound(n) nsdur += len(snd) numsnd[i] = snd } // Random intervals between digits (including beginning). intervals := make([]int, len(digits)+1) intdur := 0 for i := range intervals { dur := a.rng.Int(sampleRate, sampleRate*3) // 1 to 3 seconds intdur += dur intervals[i] = dur } // Generate background sound. bg := a.makeBackgroundSound(a.longestDigitSndLen()*len(digits) + intdur) // Create buffer and write audio to it. sil := makeSilence(sampleRate / 5) bufcap := 3*len(beepSound) + 2*len(sil) + len(bg) + len(endingBeepSound) a.body = bytes.NewBuffer(make([]byte, 0, bufcap)) // Write prelude, three beeps. a.body.Write(beepSound) a.body.Write(sil) a.body.Write(beepSound) a.body.Write(sil) a.body.Write(beepSound) // Write digits. pos := intervals[0] for i, v := range numsnd { mixSound(bg[pos:], v) pos += len(v) + intervals[i+1] } a.body.Write(bg) // Write ending (one beep). a.body.Write(endingBeepSound) return a }
其中關於語言的數據在digitSounds
map 中
看到這個,就有點像作字庫解析 和 嵌入式裏面的數據解析了
var digitSounds = map[string][][]byte{ "en": [][]byte{ { // 0 0x80, 0x7f, 0x80, 0x7f, 0x80, 0x80, 0x80, 0x7f, 0x80, 0x7f, 0x80, ... }, "ru": [][]byte{ { // 0 0x7f, 0x7f, 0x7e, 0x7f, 0x7f, 0x7e, 0x7f, 0x7e, 0x7f, 0x7f, 0x7e, ... }, "zh": [][]byte{ { // 0 0x7f, 0x80, 0x7f, 0x80, 0x80, 0x7f, 0x80, 0x7f, 0x80, 0x7f, 0x80, ... }, "ja": [][]byte{ { // 0 0x7f, 0x80, 0x7f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x83, ... },
my_captcha.html 實現以下
暫時關於音頻的語言,就寫了2種語言
<!doctype html> <head> <title>GO 簡單製做驗證碼案例</title> <style> input{ margin-top: 30px; } </style> </head> <body> <script> // 設置語言 function setSrcQuery(e, q) { var src = e.src; var p = src.indexOf('?'); if (p >= 0) { src = src.substr(0, p); } e.src = src + "?" + q } // 播放音頻 function playAudio() { var le = document.getElementById("lang"); var lang = le.options[le.selectedIndex].value; var e = document.getElementById('audio') setSrcQuery(e, "lang=" + lang) e.style.display = 'block'; e.autoplay = 'true'; return false; } // 切換語言 function changeLang() { var e = document.getElementById('audio') if (e.style.display == 'block') { playAudio(); } } // 從新加載 function reload() { setSrcQuery(document.getElementById('image'), "reload=" + (new Date()).getTime()); setSrcQuery(document.getElementById('audio'), (new Date()).getTime()); return false; } </script> <div align="center" > <select id="lang" onchange="changeLang()"> <option value="en">英文</option> <option value="zh">中文</option> </select> </div> <form action="/processCapcha" method=post align="center"> <p>請輸入你在下面的圖片中看到的數字:</p> <p><img id=image src="/captcha/{{.CaptchaId}}.png" alt="Captcha image"></p> <a href="#" onclick="reload()">從新加載</a> | <a href="#" onclick="playAudio()">播放音頻驗證碼</a> <audio id=audio controls style="display:none" src="/captcha/{{.CaptchaId}}.wav" preload=none> You browser doesn't support audio. <a href="/captcha/download/{{.CaptchaId}}.wav">下載文件</a> to play it in the external player. </audio> <input type=hidden name=captchaId value="{{.CaptchaId}}" align=center><br> <input name=captchaSolution align=center> <input type=submit value=Submit> </form>
main.go
package main import ( "github.com/dchest/captcha" "io" "io/ioutil" "log" "net/http" "text/template" ) const filePath = "./my_captcha.html" // 讀取 html 文件 func readHtml() string { var bytes []byte var err error if bytes, err = ioutil.ReadFile(filePath); err != nil { log.Fatalf("ioutil.ReadFile error filePath = %s , err :"+filePath, err) return "" } return string(bytes) } // 讀取html 文件,轉成template.Template 指針 var formTemplate = template.Must(template.New("myCaptcha").Parse(readHtml())) // 顯示驗證碼 func showCaptcha(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) return } d := struct { CaptchaId string }{ captcha.New(), } // Execute將解析後的模板應用到指定的數據對象,並將輸出寫入wr if err := formTemplate.Execute(w, &d); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } // 處理驗證碼,跳轉結果頁面 func resultPage(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") if !captcha.VerifyString(r.FormValue("captchaId"), r.FormValue("captchaSolution")) { io.WriteString(w, "錯誤的驗證碼,請從新輸入\n") } else { io.WriteString(w, "驗證嗎正確,你很棒哦!!\n") } io.WriteString(w, "<br><a href='/'>再試一下</a>") } func main() { // 簡單設置log參數 log.SetFlags(log.Lshortfile | log.LstdFlags) http.HandleFunc("/", showCaptcha) http.HandleFunc("/processCapcha", resultPage) http.Handle("/captcha/", captcha.Server(captcha.StdWidth, captcha.StdHeight)) log.Println("starting server : 8888") if err := http.ListenAndServe("localhost:8888", nil); err != nil { log.Fatal(err) } }
上述代碼的寬高 是這樣的
StdWidth = 240 StdHeight = 80
上述 HandleFunc
的回調函數是這個樣子的,以前介紹 gin 的時候有分享過, 能夠回頭看看 文章 來咱們一塊兒探究一下net/http 的代碼流程
// HandleFunc registers the handler function for the given pattern // in the DefaultServeMux. // The documentation for ServeMux explains how patterns are matched. func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) }
點擊播放音頻驗證碼的時候,能夠看到這樣的效果
該音頻,會根據咱們選擇語言,來播放不一樣的語音,讀取圖片上的數字
朋友們,你的支持和鼓勵,是我堅持分享,提升質量的動力
好了,本次就到這裏,下一次 如何使用GOLANG發送郵件
技術是開放的,咱們的心態,更應是開放的。擁抱變化,向陽而生,努力向前行。
我是小魔童哪吒,歡迎點贊關注收藏,下次見~