mirror of
https://github.com/dromara/tianai-captcha.git
synced 2026-05-07 06:04:34 +08:00
U 解决 背景图为 png 设置指定转换类型为 jpg 导致的图片变色问题
This commit is contained in:
@@ -3,16 +3,15 @@ package cloud.tianai.captcha.generator;
|
|||||||
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
|
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
|
||||||
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
|
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
|
||||||
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
|
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
|
||||||
|
import cloud.tianai.captcha.generator.common.util.ImgWriter;
|
||||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -59,7 +58,7 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
|
|||||||
* @param formatType 格式化类型
|
* @param formatType 格式化类型
|
||||||
* @return String
|
* @return String
|
||||||
*/
|
*/
|
||||||
@SneakyThrows(IOException.class)
|
@SneakyThrows(Exception.class)
|
||||||
public String transform(BufferedImage bufferedImage, String formatType) {
|
public String transform(BufferedImage bufferedImage, String formatType) {
|
||||||
// 这里判断处理一下,加一些警告日志
|
// 这里判断处理一下,加一些警告日志
|
||||||
String result = beforeTransform(bufferedImage, formatType);
|
String result = beforeTransform(bufferedImage, formatType);
|
||||||
@@ -67,7 +66,9 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
ImageIO.write(bufferedImage, formatType, byteArrayOutputStream);
|
long currentTimeMillis = System.currentTimeMillis();
|
||||||
|
ImgWriter.write(bufferedImage, formatType, byteArrayOutputStream, -1);
|
||||||
|
System.out.println("耗时:" + (System.currentTimeMillis() - currentTimeMillis));
|
||||||
//转换成字节码
|
//转换成字节码
|
||||||
byte[] data = byteArrayOutputStream.toByteArray();
|
byte[] data = byteArrayOutputStream.toByteArray();
|
||||||
String base64 = Base64.getEncoder().encodeToString(data);
|
String base64 = Base64.getEncoder().encodeToString(data);
|
||||||
|
|||||||
@@ -9,9 +9,8 @@ import java.awt.geom.Area;
|
|||||||
import java.awt.geom.CubicCurve2D;
|
import java.awt.geom.CubicCurve2D;
|
||||||
import java.awt.geom.QuadCurve2D;
|
import java.awt.geom.QuadCurve2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.ColorModel;
|
|
||||||
import java.awt.image.PixelGrabber;
|
import java.awt.image.PixelGrabber;
|
||||||
import java.awt.image.WritableRaster;
|
import java.awt.image.RenderedImage;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -25,6 +24,10 @@ import java.util.concurrent.ThreadLocalRandom;
|
|||||||
*/
|
*/
|
||||||
public class CaptchaImageUtils {
|
public class CaptchaImageUtils {
|
||||||
|
|
||||||
|
public static final String TYPE_JPG = "jpg";
|
||||||
|
public static final String TYPE_JPEG = "jpeg";
|
||||||
|
public static final String TYPE_PNG = "png";
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static BufferedImage wrapFile2BufferedImage(URL resourceImage) {
|
public static BufferedImage wrapFile2BufferedImage(URL resourceImage) {
|
||||||
if (resourceImage == null) {
|
if (resourceImage == null) {
|
||||||
@@ -133,19 +136,6 @@ public class CaptchaImageUtils {
|
|||||||
return rec;
|
return rec;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 深度拷贝图片
|
|
||||||
*
|
|
||||||
* @param bi 原图片
|
|
||||||
* @return BufferedImage
|
|
||||||
*/
|
|
||||||
public static BufferedImage deepCopyBufferedImage(BufferedImage bi) {
|
|
||||||
ColorModel cm = bi.getColorModel();
|
|
||||||
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
|
|
||||||
WritableRaster raster = bi.copyData(bi.getRaster().createCompatibleWritableRaster());
|
|
||||||
return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过模板图片抠图(不透明部分)
|
* 通过模板图片抠图(不透明部分)
|
||||||
*
|
*
|
||||||
@@ -180,6 +170,13 @@ public class CaptchaImageUtils {
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旋转图片
|
||||||
|
*
|
||||||
|
* @param bufferedimage
|
||||||
|
* @param degree
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public static BufferedImage rotateImage(final BufferedImage bufferedimage,
|
public static BufferedImage rotateImage(final BufferedImage bufferedimage,
|
||||||
final double degree) {
|
final double degree) {
|
||||||
// 得到图片宽度。
|
// 得到图片宽度。
|
||||||
@@ -463,6 +460,91 @@ public class CaptchaImageUtils {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static RenderedImage toRenderedImage(Image img) {
|
||||||
|
if (img instanceof RenderedImage) {
|
||||||
|
return (RenderedImage) img;
|
||||||
|
}
|
||||||
|
return copyImage(img, BufferedImage.TYPE_INT_RGB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换成指定类型的 BufferedImage
|
||||||
|
*
|
||||||
|
* @param image image
|
||||||
|
* @param imageType imageType
|
||||||
|
* @return BufferedImage
|
||||||
|
*/
|
||||||
|
public static BufferedImage toBufferedImage(Image image, String imageType) {
|
||||||
|
final int type = TYPE_PNG.equalsIgnoreCase(imageType)
|
||||||
|
? BufferedImage.TYPE_INT_ARGB
|
||||||
|
: BufferedImage.TYPE_INT_RGB;
|
||||||
|
return toBufferedImage(image, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换成指定类型的 BufferedImage
|
||||||
|
*
|
||||||
|
* @param image image
|
||||||
|
* @param imageType imageType
|
||||||
|
* @return BufferedImage
|
||||||
|
*/
|
||||||
|
public static BufferedImage toBufferedImage(Image image, int imageType) {
|
||||||
|
BufferedImage bufferedImage;
|
||||||
|
if (image instanceof BufferedImage) {
|
||||||
|
bufferedImage = (BufferedImage) image;
|
||||||
|
if (imageType != bufferedImage.getType()) {
|
||||||
|
bufferedImage = copyImage(image, imageType);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bufferedImage = copyImage(image, imageType);
|
||||||
|
}
|
||||||
|
return bufferedImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拷贝图片
|
||||||
|
*
|
||||||
|
* @param img img
|
||||||
|
* @param imageType imageType
|
||||||
|
* @return BufferedImage
|
||||||
|
*/
|
||||||
|
public static BufferedImage copyImage(Image img, int imageType) {
|
||||||
|
return copyImage(img, imageType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拷贝图片
|
||||||
|
*
|
||||||
|
* @param img img
|
||||||
|
* @param imageType imageType
|
||||||
|
* @param backgroundColor backgroundColor
|
||||||
|
* @return BufferedImage
|
||||||
|
*/
|
||||||
|
public static BufferedImage copyImage(Image img, int imageType, Color backgroundColor) {
|
||||||
|
final BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), imageType);
|
||||||
|
final Graphics2D bGr = createGraphics(bimage, backgroundColor);
|
||||||
|
bGr.drawImage(img, 0, 0, null);
|
||||||
|
bGr.dispose();
|
||||||
|
return bimage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建画板
|
||||||
|
*
|
||||||
|
* @param image image
|
||||||
|
* @param color color
|
||||||
|
* @return Graphics2D
|
||||||
|
*/
|
||||||
|
public static Graphics2D createGraphics(BufferedImage image, Color color) {
|
||||||
|
final Graphics2D g = image.createGraphics();
|
||||||
|
if (null != color) {
|
||||||
|
// 填充背景
|
||||||
|
g.setColor(color);
|
||||||
|
g.fillRect(0, 0, image.getWidth(), image.getHeight());
|
||||||
|
}
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 后缀是否是jpg
|
* 后缀是否是jpg
|
||||||
*
|
*
|
||||||
@@ -470,7 +552,7 @@ public class CaptchaImageUtils {
|
|||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public static boolean isJpeg(String type) {
|
public static boolean isJpeg(String type) {
|
||||||
return "jpg".equalsIgnoreCase(type) || "jpeg".equalsIgnoreCase(type);
|
return TYPE_JPG.equalsIgnoreCase(type) || TYPE_JPEG.equalsIgnoreCase(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -480,7 +562,6 @@ public class CaptchaImageUtils {
|
|||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public static boolean isPng(String type) {
|
public static boolean isPng(String type) {
|
||||||
return "png".equalsIgnoreCase(type);
|
return TYPE_PNG.equalsIgnoreCase(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
package cloud.tianai.captcha.generator.common.util;
|
||||||
|
|
||||||
|
import cloud.tianai.captcha.common.util.ObjectUtils;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
import javax.imageio.*;
|
||||||
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.ColorModel;
|
||||||
|
import java.awt.image.RenderedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: 天爱有情
|
||||||
|
* @date 2022/5/9 11:47
|
||||||
|
* @Description 拷贝from hutool(https://gitee.com/dromara/hutool/blob/v5-master/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java)
|
||||||
|
* 为了不依赖更多无用包, 单独拷贝出来
|
||||||
|
*/
|
||||||
|
public class ImgWriter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出
|
||||||
|
*
|
||||||
|
* @param image image
|
||||||
|
* @param imageType imageType
|
||||||
|
* @param destImageStream destImageStream
|
||||||
|
* @param quality quality 0~1
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean write(Image image, String imageType, OutputStream destImageStream, float quality) {
|
||||||
|
if (ObjectUtils.isEmpty(imageType)) {
|
||||||
|
imageType = CaptchaImageUtils.TYPE_JPG;
|
||||||
|
}
|
||||||
|
ImageOutputStream imageOutputStream = transformImageOutputStream(destImageStream);
|
||||||
|
final BufferedImage bufferedImage = CaptchaImageUtils.toBufferedImage(image, imageType);
|
||||||
|
final ImageWriter writer = getWriter(bufferedImage, imageType);
|
||||||
|
return write(bufferedImage, writer, imageOutputStream, quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出
|
||||||
|
*
|
||||||
|
* @param image image
|
||||||
|
* @param writer writer
|
||||||
|
* @param output output
|
||||||
|
* @param quality quality
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static boolean write(Image image, ImageWriter writer, ImageOutputStream output, float quality) {
|
||||||
|
if (writer == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
writer.setOutput(output);
|
||||||
|
final RenderedImage renderedImage = toRenderedImage(image);
|
||||||
|
// 设置质量
|
||||||
|
ImageWriteParam imgWriteParams = null;
|
||||||
|
if (quality > 0 && quality < 1) {
|
||||||
|
imgWriteParams = writer.getDefaultWriteParam();
|
||||||
|
if (imgWriteParams.canWriteCompressed()) {
|
||||||
|
imgWriteParams.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||||
|
imgWriteParams.setCompressionQuality(quality);
|
||||||
|
final ColorModel colorModel = renderedImage.getColorModel();// ColorModel.getRGBdefault();
|
||||||
|
imgWriteParams.setDestinationType(new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(16, 16)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (null != imgWriteParams) {
|
||||||
|
writer.write(null, new IIOImage(renderedImage, null, null), imgWriteParams);
|
||||||
|
} else {
|
||||||
|
writer.write(renderedImage);
|
||||||
|
}
|
||||||
|
output.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
writer.dispose();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RenderedImage toRenderedImage(Image img) {
|
||||||
|
if (img instanceof RenderedImage) {
|
||||||
|
return (RenderedImage) img;
|
||||||
|
}
|
||||||
|
return CaptchaImageUtils.copyImage(img, BufferedImage.TYPE_INT_RGB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 ImageWriter
|
||||||
|
*
|
||||||
|
* @param img img
|
||||||
|
* @param formatName formatName
|
||||||
|
* @return ImageWriter
|
||||||
|
*/
|
||||||
|
public static ImageWriter getWriter(Image img, String formatName) {
|
||||||
|
final ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(CaptchaImageUtils.toBufferedImage(img, formatName));
|
||||||
|
final Iterator<ImageWriter> iter = ImageIO.getImageWriters(type, formatName);
|
||||||
|
return iter.hasNext() ? iter.next() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 OutputStream 转换为 ImageOutputStream
|
||||||
|
*
|
||||||
|
* @param out out
|
||||||
|
* @return ImageOutputStream
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
@SneakyThrows(IOException.class)
|
||||||
|
public static ImageOutputStream transformImageOutputStream(OutputStream out) throws RuntimeException {
|
||||||
|
ImageOutputStream result = ImageIO.createImageOutputStream(out);
|
||||||
|
if (null == result) {
|
||||||
|
throw new IllegalArgumentException("Image type is not supported!");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -79,7 +79,7 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
|
|||||||
inputStreams.add(resourceInputStream);
|
inputStreams.add(resourceInputStream);
|
||||||
BufferedImage cutBackground = CaptchaImageUtils.wrapFile2BufferedImage(resourceInputStream);
|
BufferedImage cutBackground = CaptchaImageUtils.wrapFile2BufferedImage(resourceInputStream);
|
||||||
// 拷贝一份图片
|
// 拷贝一份图片
|
||||||
BufferedImage targetBackground = CaptchaImageUtils.deepCopyBufferedImage(cutBackground);
|
BufferedImage targetBackground = CaptchaImageUtils.copyImage(cutBackground, cutBackground.getType());
|
||||||
|
|
||||||
InputStream fixedTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME);
|
InputStream fixedTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME);
|
||||||
inputStreams.add(fixedTemplateInput);
|
inputStreams.add(fixedTemplateInput);
|
||||||
|
|||||||
+1
-1
@@ -69,7 +69,7 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
|
|||||||
inputStreams.add(resourceInputStream);
|
inputStreams.add(resourceInputStream);
|
||||||
BufferedImage cutBackground = CaptchaImageUtils.wrapFile2BufferedImage(resourceInputStream);
|
BufferedImage cutBackground = CaptchaImageUtils.wrapFile2BufferedImage(resourceInputStream);
|
||||||
// 拷贝一份图片
|
// 拷贝一份图片
|
||||||
BufferedImage targetBackground = CaptchaImageUtils.deepCopyBufferedImage(cutBackground);
|
BufferedImage targetBackground = CaptchaImageUtils.copyImage(cutBackground, cutBackground.getType());
|
||||||
|
|
||||||
InputStream fixedTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME);
|
InputStream fixedTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME);
|
||||||
inputStreams.add(fixedTemplateInput);
|
inputStreams.add(fixedTemplateInput);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class CaptchaGenTest {
|
|||||||
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager();
|
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager();
|
||||||
ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
|
ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
|
||||||
// 添加一些系统的资源文件
|
// 添加一些系统的资源文件
|
||||||
resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource(FileResourceProvider.NAME, "C:\\Users\\Thinkpad\\Desktop\\111\\test.png"));
|
resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource(FileResourceProvider.NAME, "C:\\Users\\Thinkpad\\Desktop\\111\\66.jpg"));
|
||||||
|
|
||||||
// 添加一些系统的 模板文件
|
// 添加一些系统的 模板文件
|
||||||
Map<String, Resource> template1 = new HashMap<>(4);
|
Map<String, Resource> template1 = new HashMap<>(4);
|
||||||
@@ -40,18 +40,20 @@ public class CaptchaGenTest {
|
|||||||
MultiImageCaptchaGenerator imageCaptchaGenerator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager, false);
|
MultiImageCaptchaGenerator imageCaptchaGenerator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager, false);
|
||||||
GenerateParam generateParam = GenerateParam.builder()
|
GenerateParam generateParam = GenerateParam.builder()
|
||||||
.type(CaptchaTypeConstant.SLIDER)
|
.type(CaptchaTypeConstant.SLIDER)
|
||||||
.backgroundFormatName("jpg")
|
.backgroundFormatName("webp")
|
||||||
.sliderFormatName("png")
|
.sliderFormatName("webp")
|
||||||
.obfuscate(false)
|
.obfuscate(false)
|
||||||
.build();
|
.build();
|
||||||
ImageCaptchaInfo imageCaptchaInfo = imageCaptchaGenerator.generateCaptchaImage(generateParam);
|
// for (int i = 0; i < 10; i++) {
|
||||||
System.out.println(imageCaptchaInfo.getBackgroundImage());
|
ImageCaptchaInfo imageCaptchaInfo = imageCaptchaGenerator.generateCaptchaImage(generateParam);
|
||||||
// System.out.println(imageCaptchaInfo.getSliderImage());
|
System.out.println(imageCaptchaInfo.getBackgroundImage());
|
||||||
|
System.out.println(imageCaptchaInfo.getSliderImage());
|
||||||
|
|
||||||
// 负责计算一些数据存到缓存中,用于校验使用
|
// 负责计算一些数据存到缓存中,用于校验使用
|
||||||
// ImageCaptchaValidator负责校验用户滑动滑块是否正确和生成滑块的一些校验数据; 比如滑块到凹槽的百分比值
|
// ImageCaptchaValidator负责校验用户滑动滑块是否正确和生成滑块的一些校验数据; 比如滑块到凹槽的百分比值
|
||||||
ImageCaptchaValidator imageCaptchaValidator = new BasicCaptchaTrackValidator();
|
ImageCaptchaValidator imageCaptchaValidator = new BasicCaptchaTrackValidator();
|
||||||
// 这个map数据应该存到缓存中,校验的时候需要用到该数据
|
// 这个map数据应该存到缓存中,校验的时候需要用到该数据
|
||||||
Map<String, Object> map = imageCaptchaValidator.generateImageCaptchaValidData(imageCaptchaInfo);
|
Map<String, Object> map = imageCaptchaValidator.generateImageCaptchaValidData(imageCaptchaInfo);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user