[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发送邮件
技术是开放的,咱们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。
我是小魔童哪吒,欢迎点赞关注收藏,下次见~