1、http://www.oschina.net/code/snippet_173630_12006 :
效果:
源代码:
1: package main
2:
3: import (
4: crand "crypto/rand"
5: "fmt"
6: "image"
7: "image/color"
8: "image/png"
9: "io"
10: "math/rand"
11: "net/http"
12: "strconv"
13: "time"
14: )
15:
16: const (
17: stdWidth = 100
18: stdHeight = 40
19: maxSkew = 2
20: )
21:
22: const (
23: fontWidth = 5
24: fontHeight = 8
25: blackChar = 1
26: )
27:
28: var font = [][]byte{
29: { // 0
30: 0, 1, 1, 1, 0,
31: 1, 0, 0, 0, 1,
32: 1, 0, 0, 0, 1,
33: 1, 0, 0, 0, 1,
34: 1, 0, 0, 0, 1,
35: 1, 0, 0, 0, 1,
36: 1, 0, 0, 0, 1,
37: 0, 1, 1, 1, 0},
38: { // 1
39: 0, 0, 1, 0, 0,
40: 0, 1, 1, 0, 0,
41: 1, 0, 1, 0, 0,
42: 0, 0, 1, 0, 0,
43: 0, 0, 1, 0, 0,
44: 0, 0, 1, 0, 0,
45: 0, 0, 1, 0, 0,
46: 1, 1, 1, 1, 1},
47: { // 2
48: 0, 1, 1, 1, 0,
49: 1, 0, 0, 0, 1,
50: 0, 0, 0, 0, 1,
51: 0, 0, 0, 1, 1,
52: 0, 1, 1, 0, 0,
53: 1, 0, 0, 0, 0,
54: 1, 0, 0, 0, 0,
55: 1, 1, 1, 1, 1},
56: { // 3
57: 1, 1, 1, 1, 0,
58: 0, 0, 0, 0, 1,
59: 0, 0, 0, 1, 0,
60: 0, 1, 1, 1, 0,
61: 0, 0, 0, 1, 0,
62: 0, 0, 0, 0, 1,
63: 0, 0, 0, 0, 1,
64: 1, 1, 1, 1, 0},
65: { // 4
66: 1, 0, 0, 1, 0,
67: 1, 0, 0, 1, 0,
68: 1, 0, 0, 1, 0,
69: 1, 0, 0, 1, 0,
70: 1, 1, 1, 1, 1,
71: 0, 0, 0, 1, 0,
72: 0, 0, 0, 1, 0,
73: 0, 0, 0, 1, 0},
74: { // 5
75: 1, 1, 1, 1, 1,
76: 1, 0, 0, 0, 0,
77: 1, 0, 0, 0, 0,
78: 1, 1, 1, 1, 0,
79: 0, 0, 0, 0, 1,
80: 0, 0, 0, 0, 1,
81: 0, 0, 0, 0, 1,
82: 1, 1, 1, 1, 0},
83: { // 6
84: 0, 0, 1, 1, 1,
85: 0, 1, 0, 0, 0,
86: 1, 0, 0, 0, 0,
87: 1, 1, 1, 1, 0,
88: 1, 0, 0, 0, 1,
89: 1, 0, 0, 0, 1,
90: 1, 0, 0, 0, 1,
91: 0, 1, 1, 1, 0},
92: { // 7
93: 1, 1, 1, 1, 1,
94: 0, 0, 0, 0, 1,
95: 0, 0, 0, 0, 1,
96: 0, 0, 0, 1, 0,
97: 0, 0, 1, 0, 0,
98: 0, 1, 0, 0, 0,
99: 0, 1, 0, 0, 0,
100: 0, 1, 0, 0, 0},
101: { // 8
102: 0, 1, 1, 1, 0,
103: 1, 0, 0, 0, 1,
104: 1, 0, 0, 0, 1,
105: 0, 1, 1, 1, 0,
106: 1, 0, 0, 0, 1,
107: 1, 0, 0, 0, 1,
108: 1, 0, 0, 0, 1,
109: 0, 1, 1, 1, 0},
110: { // 9
111: 0, 1, 1, 1, 0,
112: 1, 0, 0, 0, 1,
113: 1, 0, 0, 0, 1,
114: 1, 1, 0, 0, 1,
115: 0, 1, 1, 1, 1,
116: 0, 0, 0, 0, 1,
117: 0, 0, 0, 0, 1,
118: 1, 1, 1, 1, 0},
119: }
120:
121: type Image struct {
122: *image.NRGBA
123: color *color.NRGBA
124: width int //a digit width
125: height int //a digit height
126: dotsize int
127: }
128:
129: func init() {
130: rand.Seed(int64(time.Second))
131:
132: }
133:
134: func NewImage(digits []byte, width, height int) *Image {
135: img := new(Image)
136: r := image.Rect(img.width, img.height, stdWidth, stdHeight)
137: img.NRGBA = image.NewNRGBA(r)
138: img.color = &color.NRGBA{
139: uint8(rand.Intn(129)),
140: uint8(rand.Intn(129)),
141: uint8(rand.Intn(129)),
142: 0xFF}
143: // Draw background (10 random circles of random brightness)
144: img.calculateSizes(width, height, len(digits))
145: img.fillWithCircles(10, img.dotsize)
146: maxx := width - (img.width+img.dotsize)*len(digits) - img.dotsize
147: maxy := height - img.height - img.dotsize*2
148: x := rnd(img.dotsize*2, maxx)
149: y := rnd(img.dotsize*2, maxy)
150: // Draw digits.
151: for _, n := range digits {
152: img.drawDigit(font[n], x, y)
153: x += img.width + img.dotsize
154: }
155: // Draw strike-through line.
156: img.strikeThrough()
157: return img
158:
159: }
160:
161: func (img *Image) WriteTo(w io.Writer) (int64, error) {
162: return 0, png.Encode(w, img)
163:
164: }
165:
166: func (img *Image) calculateSizes(width, height, ncount int) {
167: // Goal: fit all digits inside the image.
168: var border int
169: if width > height {
170: border = height / 5
171: } else {
172: border = width / 5
173: }
174: // Convert everything to floats for calculations.
175: w := float64(width - border*2) //268
176: h := float64(height - border*2) //48
177: // fw takes into account 1-dot spacing between digits.
178: fw := float64(fontWidth) + 1 //6
179: fh := float64(fontHeight) //8
180: nc := float64(ncount) //7
181: // Calculate the width of a single digit taking into account only the
182: // width of the image.
183: nw := w / nc //38
184: // Calculate the height of a digit from this width.
185: nh := nw * fh / fw //51
186: // Digit too high?
187: if nh > h {
188: // Fit digits based on height.
189: nh = h //nh = 44
190: nw = fw / fh * nh
191: }
192: // Calculate dot size.
193: img.dotsize = int(nh / fh)
194: // Save everything, making the actual width smaller by 1 dot to account
195: // for spacing between digits.
196: img.width = int(nw)
197: img.height = int(nh) - img.dotsize
198:
199: }
200:
201: func (img *Image) fillWithCircles(n, maxradius int) {
202: color := img.color
203: maxx := img.Bounds().Max.X
204: maxy := img.Bounds().Max.Y
205: for i := 0; i < n; i++ {
206: setRandomBrightness(color, 255)
207: r := rnd(1, maxradius)
208: img.drawCircle(color, rnd(r, maxx-r), rnd(r, maxy-r), r)
209: }
210:
211: }
212:
213: func (img *Image) drawHorizLine(color color.Color, fromX, toX, y int) {
214: for x := fromX; x <= toX; x++ {
215: img.Set(x, y, color)
216: }
217:
218: }
219:
220: func (img *Image) drawCircle(color color.Color, x, y, radius int) {
221: f := 1 - radius
222: dfx := 1
223: dfy := -2 * radius
224: xx := 0
225: yy := radius
226: img.Set(x, y+radius, color)
227: img.Set(x, y-radius, color)
228: img.drawHorizLine(color, x-radius, x+radius, y)
229: for xx < yy {
230: if f >= 0 {
231: yy--
232: dfy += 2
233: f += dfy
234: }
235: xx++
236: dfx += 2
237: f += dfx
238: img.drawHorizLine(color, x-xx, x+xx, y+yy)
239: img.drawHorizLine(color, x-xx, x+xx, y-yy)
240: img.drawHorizLine(color, x-yy, x+yy, y+xx)
241: img.drawHorizLine(color, x-yy, x+yy, y-xx)
242: }
243:
244: }
245:
246: func (img *Image) strikeThrough() {
247: r := 0
248: maxx := img.Bounds().Max.X
249: maxy := img.Bounds().Max.Y
250: y := rnd(maxy/3, maxy-maxy/3)
251: for x := 0; x < maxx; x += r {
252: r = rnd(1, img.dotsize/3)
253: y += rnd(-img.dotsize/2, img.dotsize/2)
254: if y <= 0 || y >= maxy {
255: y = rnd(maxy/3, maxy-maxy/3)
256: }
257: img.drawCircle(img.color, x, y, r)
258: }
259:
260: }
261:
262: func (img *Image) drawDigit(digit []byte, x, y int) {
263: skf := rand.Float64() * float64(rnd(-maxSkew, maxSkew))
264: xs := float64(x)
265: minr := img.dotsize / 2 // minumum radius
266: maxr := img.dotsize/2 + img.dotsize/4 // maximum radius
267: y += rnd(-minr, minr)
268: for yy := 0; yy < fontHeight; yy++ {
269: for xx := 0; xx < fontWidth; xx++ {
270: if digit[yy*fontWidth+xx] != blackChar {
271: continue
272: }
273: // Introduce random variations.
274: or := rnd(minr, maxr)
275: ox := x + (xx * img.dotsize) + rnd(0, or/2)
276: oy := y + (yy * img.dotsize) + rnd(0, or/2)
277: img.drawCircle(img.color, ox, oy, or)
278: }
279: xs += skf
280: x = int(xs)
281: }
282:
283: }
284:
285: func setRandomBrightness(c *color.NRGBA, max uint8) {
286: minc := min3(c.R, c.G, c.B)
287: maxc := max3(c.R, c.G, c.B)
288: if maxc > max {
289: return
290: }
291: n := rand.Intn(int(max-maxc)) - int(minc)
292: c.R = uint8(int(c.R) + n)
293: c.G = uint8(int(c.G) + n)
294: c.B = uint8(int(c.B) + n)
295:
296: }
297:
298: func min3(x, y, z uint8) (o uint8) {
299: o = x
300: if y < o {
301: o = y
302: }
303: if z < o {
304: o = z
305: }
306: return
307:
308: }
309:
310: func max3(x, y, z uint8) (o uint8) {
311: o = x
312: if y > o {
313: o = y
314: }
315: if z > o {
316: o = z
317: }
318: return
319:
320: }
321:
322: // rnd returns a random number in range [from, to].
323:
324: func rnd(from, to int) int {
325: //println(to+1-from)
326: return rand.Intn(to+1-from) + from
327:
328: }
329:
330: const (
331: // Standard length of uniuri string to achive ~95 bits of entropy.
332: StdLen = 16
333: // Length of uniurl string to achive ~119 bits of entropy, closest
334: // to what can be losslessly converted to UUIDv4 (122 bits).
335: UUIDLen = 20
336: )
337:
338: // Standard characters allowed in uniuri string.
339:
340: var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
341:
342: // New returns a new random string of the standard length, consisting of
343: // standard characters.
344:
345: func New() string {
346: return NewLenChars(StdLen, StdChars)
347:
348: }
349:
350: // NewLen returns a new random string of the provided length, consisting of
351: // standard characters.
352:
353: func NewLen(length int) string {
354: return NewLenChars(length, StdChars)
355:
356: }
357:
358: // NewLenChars returns a new random string of the provided length, consisting
359: // of the provided byte slice of allowed characters (maximum 256).
360:
361: func NewLenChars(length int, chars []byte) string {
362: b := make([]byte, length)
363: r := make([]byte, length+(length/4)) // storage for random bytes.
364: clen := byte(len(chars))
365: maxrb := byte(256 - (256 % len(chars)))
366: i := 0
367: for {
368: if _, err := io.ReadFull(crand.Reader, r); err != nil {
369: panic("error reading from random source: " + err.Error())
370: }
371: for _, c := range r {
372: if c >= maxrb {
373: // Skip this number to avoid modulo bias.
374: continue
375: }
376: b[i] = chars[c%clen]
377: i++
378: if i == length {
379: return string(b)
380: }
381: }
382: }
383: panic("unreachable")
384:
385: }
386:
387: func pic(w http.ResponseWriter, req *http.Request) {
388: d := make([]byte, 4)
389: s := NewLen(4)
390: ss := ""
391: d = []byte(s)
392: for v := range d {
393: d[v] %= 10
394: ss += strconv.FormatInt(int64(d[v]), 32)
395: }
396: w.Header().Set("Content-Type", "image/png")
397: NewImage(d, 100, 40).WriteTo(w)
398: fmt.Println(ss)
399:
400: }
401:
402: func index(w http.ResponseWriter, req *http.Request) {
403: str := "<meta charset="utf-8"><h3>golang 图片验证码例子</h3><img border="1" src="/pic" alt="图片验证码" onclick="this.src='/pic'" />"
404: w.Header().Set("Content-Type", "text/html")
405: w.Write([]byte(str))
406:
407: }
408:
409: func main() {
410: http.HandleFunc("/pic", pic)
411: http.HandleFunc("/", index)
412: s := &http.Server{
413: Addr: ":8080",
414: ReadTimeout: 30 * time.Second,
415: WriteTimeout: 30 * time.Second,
416: MaxHeaderBytes: 1 << 20}
417: s.ListenAndServe()
418:
419: }
2、https://github.com/dchest/captcha
它比较强大,可以生成图片的验证码或者音频验证码。