分享
golang 图片验证码
zb_liang · · 3078 次点击 · · 开始浏览这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。
packagemain
import(
crand"crypto/rand"
"fmt"
"image"
"image/color"
"image/png"
"io"
"math/rand"
"net/http"
"strconv"
"time"
)
const(
stdWidth=100
stdHeight=40
maxSkew=2
)
const(
fontWidth=5
fontHeight=8
blackChar=1
)
varfont=[][]byte{
{//0
0,1,1,1,0,
1,0,0,0,1,
1,0,0,0,1,
1,0,0,0,1,
1,0,0,0,1,
1,0,0,0,1,
1,0,0,0,1,
0,1,1,1,0,
},
{//1
0,0,1,0,0,
0,1,1,0,0,
1,0,1,0,0,
0,0,1,0,0,
0,0,1,0,0,
0,0,1,0,0,
0,0,1,0,0,
1,1,1,1,1,
},
{//2
0,1,1,1,0,
1,0,0,0,1,
0,0,0,0,1,
0,0,0,1,1,
0,1,1,0,0,
1,0,0,0,0,
1,0,0,0,0,
1,1,1,1,1,
},
{//3
1,1,1,1,0,
0,0,0,0,1,
0,0,0,1,0,
0,1,1,1,0,
0,0,0,1,0,
0,0,0,0,1,
0,0,0,0,1,
1,1,1,1,0,
},
{//4
1,0,0,1,0,
1,0,0,1,0,
1,0,0,1,0,
1,0,0,1,0,
1,1,1,1,1,
0,0,0,1,0,
0,0,0,1,0,
0,0,0,1,0,
},
{//5
1,1,1,1,1,
1,0,0,0,0,
1,0,0,0,0,
1,1,1,1,0,
0,0,0,0,1,
0,0,0,0,1,
0,0,0,0,1,
1,1,1,1,0,
},
{//6
0,0,1,1,1,
0,1,0,0,0,
1,0,0,0,0,
1,1,1,1,0,
1,0,0,0,1,
1,0,0,0,1,
1,0,0,0,1,
0,1,1,1,0,
},
{//7
1,1,1,1,1,
0,0,0,0,1,
0,0,0,0,1,
0,0,0,1,0,
0,0,1,0,0,
0,1,0,0,0,
0,1,0,0,0,
0,1,0,0,0,
},
{//8
0,1,1,1,0,
1,0,0,0,1,
1,0,0,0,1,
0,1,1,1,0,
1,0,0,0,1,
1,0,0,0,1,
1,0,0,0,1,
0,1,1,1,0,
},
{//9
0,1,1,1,0,
1,0,0,0,1,
1,0,0,0,1,
1,1,0,0,1,
0,1,1,1,1,
0,0,0,0,1,
0,0,0,0,1,
1,1,1,1,0,
},
}
typeImagestruct{
*image.NRGBA
color*color.NRGBA
widthint//adigitwidth
heightint//adigitheight
dotsizeint
}
funcinit(){
rand.Seed(int64(time.Second))
}
funcNewImage(digits[]byte,width,heightint)*Image{
img:=new(Image)
r:=image.Rect(img.width,img.height,stdWidth,stdHeight)
img.NRGBA=image.NewNRGBA(r)
img.color=&color.NRGBA{
uint8(rand.Intn(129)),
uint8(rand.Intn(129)),
uint8(rand.Intn(129)),
0xFF,
}
//Drawbackground(10randomcirclesofrandombrightness)
img.calculateSizes(width,height,len(digits))
img.fillWithCircles(10,img.dotsize)
maxx:=width-(img.width+img.dotsize)*len(digits)-img.dotsize
maxy:=height-img.height-img.dotsize*2
x:=rnd(img.dotsize*2,maxx)
y:=rnd(img.dotsize*2,maxy)
//Drawdigits.
for_,n:=rangedigits{
img.drawDigit(font[n],x,y)
x+=img.width+img.dotsize
}
//Drawstrike-throughline.
img.strikeThrough()
returnimg
}
func(img*Image)WriteTo(wio.Writer)(int64,error){
return0,png.Encode(w,img)
}
func(img*Image)calculateSizes(width,height,ncountint){
//Goal:fitalldigitsinsidetheimage.
varborderint
ifwidth>height{
border=height/5
}else{
border=width/5
}
//Converteverythingtofloatsforcalculations.
w:=float64(width-border*2)//268
h:=float64(height-border*2)//48
//fwtakesintoaccount1-dotspacingbetweendigits.
fw:=float64(fontWidth)+1//6
fh:=float64(fontHeight)//8
nc:=float64(ncount)//7
//Calculatethewidthofasingledigittakingintoaccountonlythe
//widthoftheimage.
nw:=w/nc//38
//Calculatetheheightofadigitfromthiswidth.
nh:=nw*fh/fw//51
//Digittoohigh?
ifnh>h{
//Fitdigitsbasedonheight.
nh=h//nh=44
nw=fw/fh*nh
}
//Calculatedotsize.
img.dotsize=int(nh/fh)
//Saveeverything,makingtheactualwidthsmallerby1dottoaccount
//forspacingbetweendigits.
img.width=int(nw)
img.height=int(nh)-img.dotsize
}
func(img*Image)fillWithCircles(n,maxradiusint){
color:=img.color
maxx:=img.Bounds().Max.X
maxy:=img.Bounds().Max.Y
fori:=0;i<n;i++{
setRandomBrightness(color,255)
r:=rnd(1,maxradius)
img.drawCircle(color,rnd(r,maxx-r),rnd(r,maxy-r),r)
}
}
func(img*Image)drawHorizLine(colorcolor.Color,fromX,toX,yint){
forx:=fromX;x<=toX;x++{
img.Set(x,y,color)
}
}
func(img*Image)drawCircle(colorcolor.Color,x,y,radiusint){
f:=1-radius
dfx:=1
dfy:=-2*radius
xx:=0
yy:=radius
img.Set(x,y+radius,color)
img.Set(x,y-radius,color)
img.drawHorizLine(color,x-radius,x+radius,y)
forxx<yy{
iff>=0{
yy--
dfy+=2
f+=dfy
}
xx++
dfx+=2
f+=dfx
img.drawHorizLine(color,x-xx,x+xx,y+yy)
img.drawHorizLine(color,x-xx,x+xx,y-yy)
img.drawHorizLine(color,x-yy,x+yy,y+xx)
img.drawHorizLine(color,x-yy,x+yy,y-xx)
}
}
func(img*Image)strikeThrough(){
r:=0
maxx:=img.Bounds().Max.X
maxy:=img.Bounds().Max.Y
y:=rnd(maxy/3,maxy-maxy/3)
forx:=0;x<maxx;x+=r{
r=rnd(1,img.dotsize/3)
y+=rnd(-img.dotsize/2,img.dotsize/2)
ify<=0||y>=maxy{
y=rnd(maxy/3,maxy-maxy/3)
}
img.drawCircle(img.color,x,y,r)
}
}
func(img*Image)drawDigit(digit[]byte,x,yint){
skf:=rand.Float64()*float64(rnd(-maxSkew,maxSkew))
xs:=float64(x)
minr:=img.dotsize/2//minumumradius
maxr:=img.dotsize/2+img.dotsize/4//maximumradius
y+=rnd(-minr,minr)
foryy:=0;yy<fontHeight;yy++{
forxx:=0;xx<fontWidth;xx++{
ifdigit[yy*fontWidth+xx]!=blackChar{
continue
}
//Introducerandomvariations.
or:=rnd(minr,maxr)
ox:=x+(xx*img.dotsize)+rnd(0,or/2)
oy:=y+(yy*img.dotsize)+rnd(0,or/2)
img.drawCircle(img.color,ox,oy,or)
}
xs+=skf
x=int(xs)
}
}
funcsetRandomBrightness(c*color.NRGBA,maxuint8){
minc:=min3(c.R,c.G,c.B)
maxc:=max3(c.R,c.G,c.B)
ifmaxc>max{
return
}
n:=rand.Intn(int(max-maxc))-int(minc)
c.R=uint8(int(c.R)+n)
c.G=uint8(int(c.G)+n)
c.B=uint8(int(c.B)+n)
}
funcmin3(x,y,zuint8)(ouint8){
o=x
ify<o{
o=y
}
ifz<o{
o=z
}
return
}
funcmax3(x,y,zuint8)(ouint8){
o=x
ify>o{
o=y
}
ifz>o{
o=z
}
return
}
//rndreturnsarandomnumberinrange[from,to].
funcrnd(from,toint)int{
//println(to+1-from)
returnrand.Intn(to+1-from)+from
}
const(
//Standardlengthofuniuristringtoachive~95bitsofentropy.
StdLen=16
//Lengthofuniurlstringtoachive~119bitsofentropy,closest
//towhatcanbelosslesslyconvertedtoUUIDv4(122bits).
UUIDLen=20
)
//Standardcharactersallowedinuniuristring.
varStdChars=[]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
//Newreturnsanewrandomstringofthestandardlength,consistingof
//standardcharacters.
funcNew()string{
returnNewLenChars(StdLen,StdChars)
}
//NewLenreturnsanewrandomstringoftheprovidedlength,consistingof
//standardcharacters.
funcNewLen(lengthint)string{
returnNewLenChars(length,StdChars)
}
//NewLenCharsreturnsanewrandomstringoftheprovidedlength,consisting
//oftheprovidedbytesliceofallowedcharacters(maximum256).
funcNewLenChars(lengthint,chars[]byte)string{
b:=make([]byte,length)
r:=make([]byte,length+(length/4))//storageforrandombytes.
clen:=byte(len(chars))
maxrb:=byte(256-(256%len(chars)))
i:=0
for{
if_,err:=io.ReadFull(crand.Reader,r);err!=nil{
panic("errorreadingfromrandomsource:"+err.Error())
}
for_,c:=ranger{
ifc>=maxrb{
//Skipthisnumbertoavoidmodulobias.
continue
}
b[i]=chars[c%clen]
i++
ifi==length{
returnstring(b)
}
}
}
panic("unreachable")
}
funcpic(whttp.ResponseWriter,req*http.Request){
d:=make([]byte,4)
s:=NewLen(4)
ss:=""
d=[]byte(s)
forv:=ranged{
d[v]%=10
ss+=strconv.FormatInt(int64(d[v]),32)
}
w.Header().Set("Content-Type","image/png")
NewImage(d,100,40).WriteTo(w)
fmt.Println(ss)
}
funcindex(whttp.ResponseWriter,req*http.Request){
str:="<metacharset=\"utf-8\"><h3>golang图片验证码例子</h3><imgborder=\"1\"src=\"/pic\"alt=\"图片验证码\"onclick=\"this.src='/pic'\"/>"
w.Header().Set("Content-Type","text/html")
w.Write([]byte(str))
}
funcmain(){
http.HandleFunc("/pic",pic)
http.HandleFunc("/",index)
s:=&http.Server{
Addr:":8080",
ReadTimeout:30*time.Second,
WriteTimeout:30*time.Second,
MaxHeaderBytes:1<<20,
}
s.ListenAndServe()
}
有疑问加站长微信联系(非本文作者)
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
关注微信3078 次点击
添加一条新回复
(您需要 后才能回复 没有账号 ?)
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码` - 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传
收入到我管理的专栏 新建专栏
import(
crand"crypto/rand"
"fmt"
"image"
"image/color"
"image/png"
"io"
"math/rand"
"net/http"
"strconv"
"time"
)
const(
stdWidth=100
stdHeight=40
maxSkew=2
)
const(
fontWidth=5
fontHeight=8
blackChar=1
)
varfont=[][]byte{
{//0
0,1,1,1,0,
1,0,0,0,1,
1,0,0,0,1,
1,0,0,0,1,
1,0,0,0,1,
1,0,0,0,1,
1,0,0,0,1,
0,1,1,1,0,
},
{//1
0,0,1,0,0,
0,1,1,0,0,
1,0,1,0,0,
0,0,1,0,0,
0,0,1,0,0,
0,0,1,0,0,
0,0,1,0,0,
1,1,1,1,1,
},
{//2
0,1,1,1,0,
1,0,0,0,1,
0,0,0,0,1,
0,0,0,1,1,
0,1,1,0,0,
1,0,0,0,0,
1,0,0,0,0,
1,1,1,1,1,
},
{//3
1,1,1,1,0,
0,0,0,0,1,
0,0,0,1,0,
0,1,1,1,0,
0,0,0,1,0,
0,0,0,0,1,
0,0,0,0,1,
1,1,1,1,0,
},
{//4
1,0,0,1,0,
1,0,0,1,0,
1,0,0,1,0,
1,0,0,1,0,
1,1,1,1,1,
0,0,0,1,0,
0,0,0,1,0,
0,0,0,1,0,
},
{//5
1,1,1,1,1,
1,0,0,0,0,
1,0,0,0,0,
1,1,1,1,0,
0,0,0,0,1,
0,0,0,0,1,
0,0,0,0,1,
1,1,1,1,0,
},
{//6
0,0,1,1,1,
0,1,0,0,0,
1,0,0,0,0,
1,1,1,1,0,
1,0,0,0,1,
1,0,0,0,1,
1,0,0,0,1,
0,1,1,1,0,
},
{//7
1,1,1,1,1,
0,0,0,0,1,
0,0,0,0,1,
0,0,0,1,0,
0,0,1,0,0,
0,1,0,0,0,
0,1,0,0,0,
0,1,0,0,0,
},
{//8
0,1,1,1,0,
1,0,0,0,1,
1,0,0,0,1,
0,1,1,1,0,
1,0,0,0,1,
1,0,0,0,1,
1,0,0,0,1,
0,1,1,1,0,
},
{//9
0,1,1,1,0,
1,0,0,0,1,
1,0,0,0,1,
1,1,0,0,1,
0,1,1,1,1,
0,0,0,0,1,
0,0,0,0,1,
1,1,1,1,0,
},
}
typeImagestruct{
*image.NRGBA
color*color.NRGBA
widthint//adigitwidth
heightint//adigitheight
dotsizeint
}
funcinit(){
rand.Seed(int64(time.Second))
}
funcNewImage(digits[]byte,width,heightint)*Image{
img:=new(Image)
r:=image.Rect(img.width,img.height,stdWidth,stdHeight)
img.NRGBA=image.NewNRGBA(r)
img.color=&color.NRGBA{
uint8(rand.Intn(129)),
uint8(rand.Intn(129)),
uint8(rand.Intn(129)),
0xFF,
}
//Drawbackground(10randomcirclesofrandombrightness)
img.calculateSizes(width,height,len(digits))
img.fillWithCircles(10,img.dotsize)
maxx:=width-(img.width+img.dotsize)*len(digits)-img.dotsize
maxy:=height-img.height-img.dotsize*2
x:=rnd(img.dotsize*2,maxx)
y:=rnd(img.dotsize*2,maxy)
//Drawdigits.
for_,n:=rangedigits{
img.drawDigit(font[n],x,y)
x+=img.width+img.dotsize
}
//Drawstrike-throughline.
img.strikeThrough()
returnimg
}
func(img*Image)WriteTo(wio.Writer)(int64,error){
return0,png.Encode(w,img)
}
func(img*Image)calculateSizes(width,height,ncountint){
//Goal:fitalldigitsinsidetheimage.
varborderint
ifwidth>height{
border=height/5
}else{
border=width/5
}
//Converteverythingtofloatsforcalculations.
w:=float64(width-border*2)//268
h:=float64(height-border*2)//48
//fwtakesintoaccount1-dotspacingbetweendigits.
fw:=float64(fontWidth)+1//6
fh:=float64(fontHeight)//8
nc:=float64(ncount)//7
//Calculatethewidthofasingledigittakingintoaccountonlythe
//widthoftheimage.
nw:=w/nc//38
//Calculatetheheightofadigitfromthiswidth.
nh:=nw*fh/fw//51
//Digittoohigh?
ifnh>h{
//Fitdigitsbasedonheight.
nh=h//nh=44
nw=fw/fh*nh
}
//Calculatedotsize.
img.dotsize=int(nh/fh)
//Saveeverything,makingtheactualwidthsmallerby1dottoaccount
//forspacingbetweendigits.
img.width=int(nw)
img.height=int(nh)-img.dotsize
}
func(img*Image)fillWithCircles(n,maxradiusint){
color:=img.color
maxx:=img.Bounds().Max.X
maxy:=img.Bounds().Max.Y
fori:=0;i<n;i++{
setRandomBrightness(color,255)
r:=rnd(1,maxradius)
img.drawCircle(color,rnd(r,maxx-r),rnd(r,maxy-r),r)
}
}
func(img*Image)drawHorizLine(colorcolor.Color,fromX,toX,yint){
forx:=fromX;x<=toX;x++{
img.Set(x,y,color)
}
}
func(img*Image)drawCircle(colorcolor.Color,x,y,radiusint){
f:=1-radius
dfx:=1
dfy:=-2*radius
xx:=0
yy:=radius
img.Set(x,y+radius,color)
img.Set(x,y-radius,color)
img.drawHorizLine(color,x-radius,x+radius,y)
forxx<yy{
iff>=0{
yy--
dfy+=2
f+=dfy
}
xx++
dfx+=2
f+=dfx
img.drawHorizLine(color,x-xx,x+xx,y+yy)
img.drawHorizLine(color,x-xx,x+xx,y-yy)
img.drawHorizLine(color,x-yy,x+yy,y+xx)
img.drawHorizLine(color,x-yy,x+yy,y-xx)
}
}
func(img*Image)strikeThrough(){
r:=0
maxx:=img.Bounds().Max.X
maxy:=img.Bounds().Max.Y
y:=rnd(maxy/3,maxy-maxy/3)
forx:=0;x<maxx;x+=r{
r=rnd(1,img.dotsize/3)
y+=rnd(-img.dotsize/2,img.dotsize/2)
ify<=0||y>=maxy{
y=rnd(maxy/3,maxy-maxy/3)
}
img.drawCircle(img.color,x,y,r)
}
}
func(img*Image)drawDigit(digit[]byte,x,yint){
skf:=rand.Float64()*float64(rnd(-maxSkew,maxSkew))
xs:=float64(x)
minr:=img.dotsize/2//minumumradius
maxr:=img.dotsize/2+img.dotsize/4//maximumradius
y+=rnd(-minr,minr)
foryy:=0;yy<fontHeight;yy++{
forxx:=0;xx<fontWidth;xx++{
ifdigit[yy*fontWidth+xx]!=blackChar{
continue
}
//Introducerandomvariations.
or:=rnd(minr,maxr)
ox:=x+(xx*img.dotsize)+rnd(0,or/2)
oy:=y+(yy*img.dotsize)+rnd(0,or/2)
img.drawCircle(img.color,ox,oy,or)
}
xs+=skf
x=int(xs)
}
}
funcsetRandomBrightness(c*color.NRGBA,maxuint8){
minc:=min3(c.R,c.G,c.B)
maxc:=max3(c.R,c.G,c.B)
ifmaxc>max{
return
}
n:=rand.Intn(int(max-maxc))-int(minc)
c.R=uint8(int(c.R)+n)
c.G=uint8(int(c.G)+n)
c.B=uint8(int(c.B)+n)
}
funcmin3(x,y,zuint8)(ouint8){
o=x
ify<o{
o=y
}
ifz<o{
o=z
}
return
}
funcmax3(x,y,zuint8)(ouint8){
o=x
ify>o{
o=y
}
ifz>o{
o=z
}
return
}
//rndreturnsarandomnumberinrange[from,to].
funcrnd(from,toint)int{
//println(to+1-from)
returnrand.Intn(to+1-from)+from
}
const(
//Standardlengthofuniuristringtoachive~95bitsofentropy.
StdLen=16
//Lengthofuniurlstringtoachive~119bitsofentropy,closest
//towhatcanbelosslesslyconvertedtoUUIDv4(122bits).
UUIDLen=20
)
//Standardcharactersallowedinuniuristring.
varStdChars=[]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
//Newreturnsanewrandomstringofthestandardlength,consistingof
//standardcharacters.
funcNew()string{
returnNewLenChars(StdLen,StdChars)
}
//NewLenreturnsanewrandomstringoftheprovidedlength,consistingof
//standardcharacters.
funcNewLen(lengthint)string{
returnNewLenChars(length,StdChars)
}
//NewLenCharsreturnsanewrandomstringoftheprovidedlength,consisting
//oftheprovidedbytesliceofallowedcharacters(maximum256).
funcNewLenChars(lengthint,chars[]byte)string{
b:=make([]byte,length)
r:=make([]byte,length+(length/4))//storageforrandombytes.
clen:=byte(len(chars))
maxrb:=byte(256-(256%len(chars)))
i:=0
for{
if_,err:=io.ReadFull(crand.Reader,r);err!=nil{
panic("errorreadingfromrandomsource:"+err.Error())
}
for_,c:=ranger{
ifc>=maxrb{
//Skipthisnumbertoavoidmodulobias.
continue
}
b[i]=chars[c%clen]
i++
ifi==length{
returnstring(b)
}
}
}
panic("unreachable")
}
funcpic(whttp.ResponseWriter,req*http.Request){
d:=make([]byte,4)
s:=NewLen(4)
ss:=""
d=[]byte(s)
forv:=ranged{
d[v]%=10
ss+=strconv.FormatInt(int64(d[v]),32)
}
w.Header().Set("Content-Type","image/png")
NewImage(d,100,40).WriteTo(w)
fmt.Println(ss)
}
funcindex(whttp.ResponseWriter,req*http.Request){
str:="<metacharset=\"utf-8\"><h3>golang图片验证码例子</h3><imgborder=\"1\"src=\"/pic\"alt=\"图片验证码\"onclick=\"this.src='/pic'\"/>"
w.Header().Set("Content-Type","text/html")
w.Write([]byte(str))
}
funcmain(){
http.HandleFunc("/pic",pic)
http.HandleFunc("/",index)
s:=&http.Server{
Addr:":8080",
ReadTimeout:30*time.Second,
WriteTimeout:30*time.Second,
MaxHeaderBytes:1<<20,
}
s.ListenAndServe()
}