java生成gif动图验证码

  1. import com.mortals.framework.util.SecurityImage;
  2. import com.mortals.iot.module.common.web.CommonController;
  3. import com.mortals.iot.module.system.base.service.GifSecurityImage;
  4. import com.mortals.iot.module.system.valid.service.ValidCodeService;
  5. import org.apache.commons.io.IOUtils;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.RestController;
  9. import javax.servlet.http.HttpServletRequest;
  10. import javax.servlet.http.HttpServletResponse;
  11. import java.awt.image.BufferedImage;
  12. @RestController
  13. @RequestMapping("securitycode")
  14. public class SecurityCodeController extends CommonController {
  15. @Autowired
  16. private ValidCodeService validCodeService;
  17. @RequestMapping("createCode")
  18. public void createCode(HttpServletRequest request, HttpServletResponse response) {
  19. // 获取默认难度和长度的验证码
  20. String securityCode = "7834";
  21. // 默认图验
  22. byte[] content = GifSecurityImage.createGifImage(securityCode);
  23. doResponseGif(response, content);
  24. }
  25. protected void doResponseGif(HttpServletResponse response, byte[] content) {
  26. try {
  27. response.setHeader("Pragma", "No-cache");
  28. response.setHeader("Cache-Control", "No-cache");
  29. response.setDateHeader("Expires", 0);
  30. response.setContentType("image/gif");
  31. IOUtils.write(content, response.getOutputStream());
  32. } catch (Exception e) {
  33. log.error("验证码图片传输异常-->" + e.getMessage());
  34. }
  35. }
  36. }
  37. import java.awt.Color;
  38. import java.awt.Font;
  39. import java.awt.Graphics;
  40. import java.awt.image.BufferedImage;
  41. import java.io.ByteArrayInputStream;
  42. import java.io.ByteArrayOutputStream;
  43. import java.util.Random;
  44. import javax.imageio.ImageIO;
  45. import lombok.extern.slf4j.Slf4j;
  46. /**
  47. * gif动态图验证码图片生成器
  48. *
  49. */
  50. @Slf4j
  51. public class GifSecurityImage {
  52. public static BufferedImage createImage(String securityCode) {
  53. int codeLength = securityCode.length();
  54. int fSize = 14;
  55. int fWidth = fSize + 1;
  56. int width = codeLength * fWidth + 6;
  57. int height = fSize * 2 + 1;
  58. BufferedImage image = new BufferedImage(width, height, 1);
  59. Graphics g = image.createGraphics();
  60. g.setColor(Color.WHITE);
  61. g.fillRect(0, 0, width, height);
  62. g.setColor(Color.LIGHT_GRAY);
  63. g.setFont(new Font("Arial", 1, height - 2));
  64. Random rand = new Random();
  65. // 画点
  66. g.setColor(Color.LIGHT_GRAY);
  67. for (int i = 0; i < codeLength * 10; i++) {
  68. g.setColor(randomColor());
  69. int x = rand.nextInt(width);
  70. int y = rand.nextInt(height);
  71. g.drawOval(x, y, 1, 1);
  72. }
  73. for (int i = 0; i < 5; i++) {
  74. g.setColor(randomColor());
  75. int x = rand.nextInt(width - 1);
  76. int y = rand.nextInt(height - 1);
  77. int xl = rand.nextInt(6) + 1;
  78. int yl = rand.nextInt(12) + 1;
  79. g.drawLine(x, y, xl, yl);
  80. }
  81. // 写字
  82. g.setFont(new Font("Georgia", 1, fSize));
  83. for (int i = 0; i < codeLength; i++) {
  84. int codeY = height - 8 - rand.nextInt(5);
  85. int codeX = width / 4 * i + (width / 4 - fSize) / 2;
  86. g.drawString(String.valueOf(securityCode.charAt(i)), codeX, codeY);
  87. }
  88. g.dispose();
  89. return image;
  90. }
  91. public static byte[] createGifImage(String securityCode) {
  92. ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  93. AnimatedGifEncoder gifEncoder = new AnimatedGifEncoder();
  94. gifEncoder.setRepeat(0);
  95. gifEncoder.start(outputStream);
  96. for (int j = 0; j < 4; j++) {
  97. gifEncoder.setDelay(200); // 设置播放的延迟时间
  98. gifEncoder.addFrame(createImage(securityCode));
  99. }
  100. gifEncoder.finish();
  101. return outputStream.toByteArray();
  102. }
  103. private static Color randomColor() {
  104. Random random = new Random();
  105. int r = random.nextInt(256);
  106. int g = random.nextInt(256);
  107. int b = random.nextInt(256);
  108. return new Color(r, g, b);
  109. }
  110. public static ByteArrayInputStream getImageAsInputStream(String securityCode) {
  111. BufferedImage image = createImage(securityCode);
  112. return convertImageToStream(image);
  113. }
  114. private static ByteArrayInputStream convertImageToStream(BufferedImage image) {
  115. ByteArrayInputStream inputStream = null;
  116. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  117. try {
  118. byte[] bts = bos.toByteArray();
  119. inputStream = new ByteArrayInputStream(bts);
  120. ImageIO.write(image, "JPG", bos);
  121. } catch (Exception e) {
  122. log.debug("转换图片数据流异常-->" + e.getMessage());
  123. }
  124. return inputStream;
  125. }
  126. }
  127. import java.awt.Color;
  128. import java.awt.Graphics2D;
  129. import java.awt.image.BufferedImage;
  130. import java.awt.image.DataBufferByte;
  131. import java.io.BufferedOutputStream;
  132. import java.io.FileOutputStream;
  133. import java.io.IOException;
  134. import java.io.OutputStream;
  135. /**
  136. * Class AnimatedGifEncoder - Encodes a GIF file consisting of one or
  137. * more frames.
  138. * <pre>
  139. * Example:
  140. * AnimatedGifEncoder e = new AnimatedGifEncoder();
  141. * e.start(outputFileName);
  142. * e.setDelay(1000); // 1 frame per sec
  143. * e.addFrame(image1);
  144. * e.addFrame(image2);
  145. * e.finish();
  146. * </pre>
  147. * No copyright asserted on the source code of this class. May be used
  148. * for any purpose, however, refer to the Unisys LZW patent for restrictions
  149. * on use of the associated LZWEncoder class. Please forward any corrections
  150. * to kweiner@fmsware.com.
  151. *
  152. * @author Kevin Weiner, FM Software
  153. * @version 1.03 November 2003
  154. *
  155. */
  156. public class AnimatedGifEncoder {
  157. protected int width; // image size
  158. protected int height;
  159. protected Color transparent = null; // transparent color if given
  160. protected int transIndex; // transparent index in color table
  161. protected int repeat = -1; // no repeat
  162. protected int delay = 0; // frame delay (hundredths)
  163. protected boolean started = false; // ready to output frames
  164. protected OutputStream out;
  165. protected BufferedImage image; // current frame
  166. protected byte[] pixels; // BGR byte array from frame
  167. protected byte[] indexedPixels; // converted frame indexed to palette
  168. protected int colorDepth; // number of bit planes
  169. protected byte[] colorTab; // RGB palette
  170. protected boolean[] usedEntry = new boolean[256]; // active palette entries
  171. protected int palSize = 7; // color table size (bits-1)
  172. protected int dispose = -1; // disposal code (-1 = use default)
  173. protected boolean closeStream = false; // close stream when finished
  174. protected boolean firstFrame = true;
  175. protected boolean sizeSet = false; // if false, get size from first frame
  176. protected int sample = 10; // default sample interval for quantizer
  177. /**
  178. * Sets the delay time between each frame, or changes it
  179. * for subsequent frames (applies to last frame added).
  180. *
  181. * @param ms int delay time in milliseconds
  182. */
  183. public void setDelay(int ms) {
  184. delay = Math.round(ms / 10.0f);
  185. }
  186. /**
  187. * Sets the GIF frame disposal code for the last added frame
  188. * and any subsequent frames. Default is 0 if no transparent
  189. * color has been set, otherwise 2.
  190. * @param code int disposal code.
  191. */
  192. public void setDispose(int code) {
  193. if (code >= 0) {
  194. dispose = code;
  195. }
  196. }
  197. /**
  198. * Sets the number of times the set of GIF frames
  199. * should be played. Default is 1; 0 means play
  200. * indefinitely. Must be invoked before the first
  201. * image is added.
  202. *
  203. * @param iter int number of iterations.
  204. * @return
  205. */
  206. public void setRepeat(int iter) {
  207. if (iter >= 0) {
  208. repeat = iter;
  209. }
  210. }
  211. /**
  212. * Sets the transparent color for the last added frame
  213. * and any subsequent frames.
  214. * Since all colors are subject to modification
  215. * in the quantization process, the color in the final
  216. * palette for each frame closest to the given color
  217. * becomes the transparent color for that frame.
  218. * May be set to null to indicate no transparent color.
  219. *
  220. * @param c Color to be treated as transparent on display.
  221. */
  222. public void setTransparent(Color c) {
  223. transparent = c;
  224. }
  225. /**
  226. * Adds next GIF frame. The frame is not written immediately, but is
  227. * actually deferred until the next frame is received so that timing
  228. * data can be inserted. Invoking <code>finish()</code> flushes all
  229. * frames. If <code>setSize</code> was not invoked, the size of the
  230. * first image is used for all subsequent frames.
  231. *
  232. * @param im BufferedImage containing frame to write.
  233. * @return true if successful.
  234. */
  235. public boolean addFrame(BufferedImage im) {
  236. if ((im == null) || !started) {
  237. return false;
  238. }
  239. boolean ok = true;
  240. try {
  241. if (!sizeSet) {
  242. // use first frame's size
  243. setSize(im.getWidth(), im.getHeight());
  244. }
  245. image = im;
  246. getImagePixels(); // convert to correct format if necessary
  247. analyzePixels(); // build color table & map pixels
  248. if (firstFrame) {
  249. writeLSD(); // logical screen descriptior
  250. writePalette(); // global color table
  251. if (repeat >= 0) {
  252. // use NS app extension to indicate reps
  253. writeNetscapeExt();
  254. }
  255. }
  256. writeGraphicCtrlExt(); // write graphic control extension
  257. writeImageDesc(); // image descriptor
  258. if (!firstFrame) {
  259. writePalette(); // local color table
  260. }
  261. writePixels(); // encode and write pixel data
  262. firstFrame = false;
  263. } catch (IOException e) {
  264. ok = false;
  265. }
  266. return ok;
  267. }
  268. /**
  269. * Flushes any pending data and closes output file.
  270. * If writing to an OutputStream, the stream is not
  271. * closed.
  272. */
  273. public boolean finish() {
  274. if (!started) return false;
  275. boolean ok = true;
  276. started = false;
  277. try {
  278. out.write(0x3b); // gif trailer
  279. out.flush();
  280. if (closeStream) {
  281. out.close();
  282. }
  283. } catch (IOException e) {
  284. ok = false;
  285. }
  286. // reset for subsequent use
  287. transIndex = 0;
  288. out = null;
  289. image = null;
  290. pixels = null;
  291. indexedPixels = null;
  292. colorTab = null;
  293. closeStream = false;
  294. firstFrame = true;
  295. return ok;
  296. }
  297. /**
  298. * Sets frame rate in frames per second. Equivalent to
  299. * <code>setDelay(1000/fps)</code>.
  300. *
  301. * @param fps float frame rate (frames per second)
  302. */
  303. public void setFrameRate(float fps) {
  304. if (fps != 0f) {
  305. delay = Math.round(100f / fps);
  306. }
  307. }
  308. /**
  309. * Sets quality of color quantization (conversion of images
  310. * to the maximum 256 colors allowed by the GIF specification).
  311. * Lower values (minimum = 1) produce better colors, but slow
  312. * processing significantly. 10 is the default, and produces
  313. * good color mapping at reasonable speeds. Values greater
  314. * than 20 do not yield significant improvements in speed.
  315. *
  316. * @param quality int greater than 0.
  317. * @return
  318. */
  319. public void setQuality(int quality) {
  320. if (quality < 1) quality = 1;
  321. sample = quality;
  322. }
  323. /**
  324. * Sets the GIF frame size. The default size is the
  325. * size of the first frame added if this method is
  326. * not invoked.
  327. *
  328. * @param w int frame width.
  329. * @param h int frame width.
  330. */
  331. public void setSize(int w, int h) {
  332. if (started && !firstFrame) return;
  333. width = w;
  334. height = h;
  335. if (width < 1) width = 320;
  336. if (height < 1) height = 240;
  337. sizeSet = true;
  338. }
  339. /**
  340. * Initiates GIF file creation on the given stream. The stream
  341. * is not closed automatically.
  342. *
  343. * @param os OutputStream on which GIF images are written.
  344. * @return false if initial write failed.
  345. */
  346. public boolean start(OutputStream os) {
  347. if (os == null) return false;
  348. boolean ok = true;
  349. closeStream = false;
  350. out = os;
  351. try {
  352. writeString("GIF89a"); // header
  353. } catch (IOException e) {
  354. ok = false;
  355. }
  356. return started = ok;
  357. }
  358. /**
  359. * Initiates writing of a GIF file with the specified name.
  360. *
  361. * @param file String containing output file name.
  362. * @return false if open or initial write failed.
  363. */
  364. public boolean start(String file) {
  365. boolean ok = true;
  366. try {
  367. out = new BufferedOutputStream(new FileOutputStream(file));
  368. ok = start(out);
  369. closeStream = true;
  370. } catch (IOException e) {
  371. ok = false;
  372. }
  373. return started = ok;
  374. }
  375. /**
  376. * Analyzes image colors and creates color map.
  377. */
  378. protected void analyzePixels() {
  379. int len = pixels.length;
  380. int nPix = len / 3;
  381. indexedPixels = new byte[nPix];
  382. NeuQuant nq = new NeuQuant(pixels, len, sample);
  383. // initialize quantizer
  384. colorTab = nq.process(); // create reduced palette
  385. // convert map from BGR to RGB
  386. for (int i = 0; i < colorTab.length; i += 3) {
  387. byte temp = colorTab[i];
  388. colorTab[i] = colorTab[i + 2];
  389. colorTab[i + 2] = temp;
  390. usedEntry[i / 3] = false;
  391. }
  392. // map image pixels to new palette
  393. int k = 0;
  394. for (int i = 0; i < nPix; i++) {
  395. int index =
  396. nq.map(pixels[k++] & 0xff,
  397. pixels[k++] & 0xff,
  398. pixels[k++] & 0xff);
  399. usedEntry[index] = true;
  400. indexedPixels[i] = (byte) index;
  401. }
  402. pixels = null;
  403. colorDepth = 8;
  404. palSize = 7;
  405. // get closest match to transparent color if specified
  406. if (transparent != null) {
  407. transIndex = findClosest(transparent);
  408. }
  409. }
  410. /**
  411. * Returns index of palette color closest to c
  412. *
  413. */
  414. protected int findClosest(Color c) {
  415. if (colorTab == null) return -1;
  416. int r = c.getRed();
  417. int g = c.getGreen();
  418. int b = c.getBlue();
  419. int minpos = 0;
  420. int dmin = 256 * 256 * 256;
  421. int len = colorTab.length;
  422. for (int i = 0; i < len;) {
  423. int dr = r - (colorTab[i++] & 0xff);
  424. int dg = g - (colorTab[i++] & 0xff);
  425. int db = b - (colorTab[i] & 0xff);
  426. int d = dr * dr + dg * dg + db * db;
  427. int index = i / 3;
  428. if (usedEntry[index] && (d < dmin)) {
  429. dmin = d;
  430. minpos = index;
  431. }
  432. i++;
  433. }
  434. return minpos;
  435. }
  436. /**
  437. * Extracts image pixels into byte array "pixels"
  438. */
  439. protected void getImagePixels() {
  440. int w = image.getWidth();
  441. int h = image.getHeight();
  442. int type = image.getType();
  443. if ((w != width)
  444. || (h != height)
  445. || (type != BufferedImage.TYPE_3BYTE_BGR)) {
  446. // create new image with right size/format
  447. BufferedImage temp =
  448. new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
  449. Graphics2D g = temp.createGraphics();
  450. g.drawImage(image, 0, 0, null);
  451. image = temp;
  452. }
  453. pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
  454. }
  455. /**
  456. * Writes Graphic Control Extension
  457. */
  458. protected void writeGraphicCtrlExt() throws IOException {
  459. out.write(0x21); // extension introducer
  460. out.write(0xf9); // GCE label
  461. out.write(4); // data block size
  462. int transp, disp;
  463. if (transparent == null) {
  464. transp = 0;
  465. disp = 0; // dispose = no action
  466. } else {
  467. transp = 1;
  468. disp = 2; // force clear if using transparent color
  469. }
  470. if (dispose >= 0) {
  471. disp = dispose & 7; // user override
  472. }
  473. disp <<= 2;
  474. // packed fields
  475. out.write(0 | // 1:3 reserved
  476. disp | // 4:6 disposal
  477. 0 | // 7 user input - 0 = none
  478. transp); // 8 transparency flag
  479. writeShort(delay); // delay x 1/100 sec
  480. out.write(transIndex); // transparent color index
  481. out.write(0); // block terminator
  482. }
  483. /**
  484. * Writes Image Descriptor
  485. */
  486. protected void writeImageDesc() throws IOException {
  487. out.write(0x2c); // image separator
  488. writeShort(0); // image position x,y = 0,0
  489. writeShort(0);
  490. writeShort(width); // image size
  491. writeShort(height);
  492. // packed fields
  493. if (firstFrame) {
  494. // no LCT - GCT is used for first (or only) frame
  495. out.write(0);
  496. } else {
  497. // specify normal LCT
  498. out.write(0x80 | // 1 local color table 1=yes
  499. 0 | // 2 interlace - 0=no
  500. 0 | // 3 sorted - 0=no
  501. 0 | // 4-5 reserved
  502. palSize); // 6-8 size of color table
  503. }
  504. }
  505. /**
  506. * Writes Logical Screen Descriptor
  507. */
  508. protected void writeLSD() throws IOException {
  509. // logical screen size
  510. writeShort(width);
  511. writeShort(height);
  512. // packed fields
  513. out.write((0x80 | // 1 : global color table flag = 1 (gct used)
  514. 0x70 | // 2-4 : color resolution = 7
  515. 0x00 | // 5 : gct sort flag = 0
  516. palSize)); // 6-8 : gct size
  517. out.write(0); // background color index
  518. out.write(0); // pixel aspect ratio - assume 1:1
  519. }
  520. /**
  521. * Writes Netscape application extension to define
  522. * repeat count.
  523. */
  524. protected void writeNetscapeExt() throws IOException {
  525. out.write(0x21); // extension introducer
  526. out.write(0xff); // app extension label
  527. out.write(11); // block size
  528. writeString("NETSCAPE" + "2.0"); // app id + auth code
  529. out.write(3); // sub-block size
  530. out.write(1); // loop sub-block id
  531. writeShort(repeat); // loop count (extra iterations, 0=repeat forever)
  532. out.write(0); // block terminator
  533. }
  534. /**
  535. * Writes color table
  536. */
  537. protected void writePalette() throws IOException {
  538. out.write(colorTab, 0, colorTab.length);
  539. int n = (3 * 256) - colorTab.length;
  540. for (int i = 0; i < n; i++) {
  541. out.write(0);
  542. }
  543. }
  544. /**
  545. * Encodes and writes pixel data
  546. */
  547. protected void writePixels() throws IOException {
  548. LZWEncoder encoder =
  549. new LZWEncoder(width, height, indexedPixels, colorDepth);
  550. encoder.encode(out);
  551. }
  552. /**
  553. * Write 16-bit value to output stream, LSB first
  554. */
  555. protected void writeShort(int value) throws IOException {
  556. out.write(value & 0xff);
  557. out.write((value >> 8) & 0xff);
  558. }
  559. /**
  560. * Writes string to output stream
  561. */
  562. protected void writeString(String s) throws IOException {
  563. for (int i = 0; i < s.length(); i++) {
  564. out.write((byte) s.charAt(i));
  565. }
  566. }
  567. }

发表评论

表情:
评论列表 (有 0 条评论,59人围观)

还没有评论,来说两句吧...

相关阅读