Create image captcha
Posted seliote
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Create image captcha相关的知识,希望对你有一定的参考价值。
Like this:
package com.seliote.smsbomber.service.impl;
import com.seliote.smsbomber.pojo.soo.captcha.CaptchaSoo;
import com.seliote.smsbomber.service.CaptchaService;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Instant;
import java.util.Random;
/**
* CaptchaService implementation
*
* @author seliote
* @since 2020-07-06
*/
@Service
public class CaptchaServiceImpl implements CaptchaService {
// Image width = textLength * widthFactor
private final int widthFactor = 2;
// Image height = fontSize * heightFactor
@SuppressWarnings("FieldCanBeLocal")
private final float heightFactor = 2F;
// Captcha text length
private final int textLength = 4;
// Interference line count = text length * interferenceLineCountFactor
@SuppressWarnings("FieldCanBeLocal")
private final int interferenceLineCountFactor = 10;
// Interference line width = image height * interferenceLineWidthFactor
@SuppressWarnings("FieldCanBeLocal")
private final float interferenceLineWidthFactor = 0.03F;
// Text rotate angle
@SuppressWarnings("FieldCanBeLocal")
private final int textAngle = 45;
// Captcha text range
private final char[] textRange = "23456789qazwsxedcrfvtgbyhnujmikpQAZWSXEDCRFVTGBYHNUJMKLP" .toCharArray();
// Captcha text color
private final Color[] textColor = {Color.WHITE, Color.BLACK, Color.GREEN, Color.ORANGE,
Color.YELLOW, Color.RED, Color.PINK, Color.BLUE};
// Use for get random number
private final Random random = new Random(Instant.now().getNano());
@Override
public CaptchaSoo genCaptcha(int size) throws IOException {
String text = randomText();
BufferedImage bufferedImage = createImage(size);
Graphics2D graphics2D = createGraphics2D(bufferedImage);
fillImage(bufferedImage, graphics2D);
drawText(text, bufferedImage, graphics2D, size);
drawInterferenceLines(bufferedImage, graphics2D);
disposeGraphics2D(graphics2D);
return new CaptchaSoo(text, img2Bytes(bufferedImage));
}
/**
* Create an empty image
*
* @param size Font size, use to calculate image size
* @return BufferedImage object
*/
private BufferedImage createImage(int size) {
final int width = Math.round(size * textLength) + size * widthFactor;
final int height = Math.round(size * heightFactor);
return new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
}
/**
* Create Graphics2D object
*
* @param bufferedImage BufferedImage object which want get Graphics2D
* @return Graphics2D object
*/
private Graphics2D createGraphics2D(BufferedImage bufferedImage) {
return bufferedImage.createGraphics();
}
/**
* Dispose Graphics2D object
*
* @param graphics2D Graphics2D object which want to dispose
*/
private void disposeGraphics2D(Graphics2D graphics2D) {
graphics2D.dispose();
}
/**
* Fill image background
*
* @param bufferedImage BufferedImage object
* @param graphics2D Graphics2D object
*/
private void fillImage(BufferedImage bufferedImage, Graphics2D graphics2D) {
graphics2D.setColor(Color.DARK_GRAY);
graphics2D.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
}
/**
* Convert a image to byte array
*
* @param bufferedImage BufferedImage object
* @return Byte array from BufferedImage object
* @throws IOException When convert occur exception
*/
private byte[] img2Bytes(BufferedImage bufferedImage) throws IOException {
final String imageType = "JPEG";
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, imageType, byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
}
/**
* Create a random text for captcha
*
* @return Random text
*/
private String randomText() {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < textLength; ++i) {
stringBuilder.append(textRange[random.nextInt(textRange.length)]);
}
return stringBuilder.toString();
}
/**
* Draw text for captcha
*
* @param text Text want to draw
* @param bufferedImage BufferedImage object
* @param graphics2D Graphics2D object use to draw
* @param size Text font size
*/
private void drawText(String text, BufferedImage bufferedImage, Graphics2D graphics2D, int size) {
Assert.isTrue(text.length() == textLength, "Text length incorrect");
double angle2Radian = Math.PI / 180;
int imageWidth = bufferedImage.getWidth();
int imageHeight = bufferedImage.getHeight();
int eachCharWidth = imageWidth / textLength;
for (int i = 0; i < textLength; ++i) {
graphics2D.setColor(textColor[random.nextInt(textColor.length)]);
graphics2D.setFont(new Font(null, random.nextInt(2) == 0 ? Font.ITALIC : Font.BOLD, size));
int xPoint = i * eachCharWidth + Math.max((imageWidth - size * textLength), 0) / (widthFactor * 2);
int yPoint = Math.round((imageHeight - size) / (heightFactor * 2)) + size;
graphics2D.translate(xPoint, yPoint);
int angle = random.nextInt(textAngle * 2) - textAngle;
double radian = angle * angle2Radian;
graphics2D.rotate(radian);
graphics2D.drawString(text.substring(i, i + 1), 0, 0);
graphics2D.rotate(-radian);
graphics2D.translate(-xPoint, -yPoint);
}
}
/**
* Draw interference lines for captcha
*
* @param bufferedImage BufferImage object
* @param graphics2D Graphics2D object
*/
private void drawInterferenceLines(BufferedImage bufferedImage, Graphics2D graphics2D) {
int interferenceLineCount = textLength * interferenceLineCountFactor;
int interferenceLineWidth = Math.round(bufferedImage.getHeight() * interferenceLineWidthFactor);
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
for (int i = 0; i < interferenceLineCount; ++i) {
graphics2D.setColor(textColor[random.nextInt(textColor.length)]);
graphics2D.setStroke(new BasicStroke(random.nextInt(interferenceLineWidth)));
graphics2D.drawLine(random.nextInt(width), random.nextInt(height),
random.nextInt(width), random.nextInt(height));
}
}
}
Test:
package com.seliote.smsbomber.service.impl;
import com.seliote.smsbomber.config.AppContext;
import com.seliote.smsbomber.config.RootContext;
import com.seliote.smsbomber.pojo.soo.captcha.CaptchaSoo;
import com.seliote.smsbomber.service.CaptchaService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.Instant;
/**
* CaptchaServiceImpl test
*
* @author seliote
* @since 2020-07-05
*/
@DisplayName("CaptchaServiceImpl")
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = {RootContext.class, AppContext.class})
class CaptchaServiceImplTest {
private CaptchaService captchaService;
@Autowired
public void setCaptchaService(CaptchaService captchaService) {
this.captchaService = captchaService;
}
@DisplayName("genCaptcha")
@Test
void genCaptcha() throws IOException {
for (int i = 0; i < 10; ++i) {
CaptchaSoo captchaSoo = captchaService.genCaptcha(100);
try (FileOutputStream fileOutputStream = new FileOutputStream(
new File(String.format("/home/seliote/pictures/%s.jpeg", Instant.now().getNano())))) {
fileOutputStream.write(captchaSoo.getImage());
}
}
}
}
以上是关于Create image captcha的主要内容,如果未能解决你的问题,请参考以下文章