这里记录下图片验证码的生成,方便日后自己回忆。
图片工具类
/**
* 图片验证码工具类
*
* @author xiongxiaoyang
* @date 2022/5/17
*/
@UtilityClass
public class ImgVerifyCodeUtils {
/**
* 随机产生只有数字的字符串
*/
private final String randNumber = "0123456789";
/**
* 图片宽
*/
private final int width = 100;
/**
* 图片高
*/
private final int height = 38;
private final Random random = new Random();
/**
* 获得字体
*/
private Font getFont() {
return new Font("Fixed", Font.PLAIN, 23);
}
/**
* 生成校验码图片
*/
public String genVerifyCodeImg(String verifyCode) throws IOException {
// BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
// 产生Image对象的Graphics对象,改对象可以在图像上进行各种绘制操作
Graphics g = image.getGraphics();
//图片大小
g.fillRect(0, 0, width, height);
//字体大小
//字体颜色
g.setColor(new Color(204, 204, 204));
// 绘制干扰线
// 干扰线数量
int lineSize = 40;
for (int i = 0; i <= lineSize; i++) {
drawLine(g);
}
// 绘制随机字符
drawString(g, verifyCode);
g.dispose();
//将图片转换成Base64字符串
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ImageIO.write(image, "JPEG", stream);
return Base64.getEncoder().encodeToString(stream.toByteArray());
}
/**
* 绘制字符串
*/
private void drawString(Graphics g, String verifyCode) {
for (int i = 1; i <= verifyCode.length(); i++) {
g.setFont(getFont());
g.setColor(new Color(random.nextInt(101), random.nextInt(111), random
.nextInt(121)));
g.translate(random.nextInt(3), random.nextInt(3));
g.drawString(String.valueOf(verifyCode.charAt(i - 1)), 13 * i, 23);
}
}
/**
* 绘制干扰线
*/
private void drawLine(Graphics g) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(13);
int yl = random.nextInt(15);
g.drawLine(x, y, x + xl, y + yl);
}
/**
* 获取随机的校验码
*/
public String getRandomVerifyCode(int num) {
int randNumberSize = randNumber.length();
StringBuilder verifyCode = new StringBuilder();
for (int i = 0; i < num; i++) {
String rand = String.valueOf(randNumber.charAt(random.nextInt(randNumberSize)));
verifyCode.append(rand);
}
return verifyCode.toString();
}
}
验证码管理类
/**
* 验证码 管理类
*
* @author xiongxiaoyang
* @date 2022/5/12
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class VerifyCodeManager {
private final StringRedisTemplate stringRedisTemplate;
/**
* 生成图形验证码,并放入 Redis 中
*/
public String genImgVerifyCode(String sessionId) throws IOException {
String verifyCode = ImgVerifyCodeUtils.getRandomVerifyCode(4);
String img = ImgVerifyCodeUtils.genVerifyCodeImg(verifyCode);
stringRedisTemplate.opsForValue().set(CacheConsts.IMG_VERIFY_CODE_CACHE_KEY + sessionId
, verifyCode, Duration.ofMinutes(5));
return img;
}
/**
* 校验图形验证码
*/
public boolean imgVerifyCodeOk(String sessionId, String verifyCode) {
return Objects.equals(
stringRedisTemplate.opsForValue().get(CacheConsts.IMG_VERIFY_CODE_CACHE_KEY + sessionId)
, verifyCode);
}
/**
* 从 Redis 中删除验证码
*/
public void removeImgVerifyCode(String sessionId) {
stringRedisTemplate.delete(CacheConsts.IMG_VERIFY_CODE_CACHE_KEY + sessionId);
}
Service层
/**
* 资源(图片/视频/文档)相关服务类
*
* @author xiongxiaoyang
* @date 2022/5/17
*/
public interface ResourceService {
/**
* 获取图片验证码
*
* @throws IOException 验证码图片生成失败
* @return Base64编码的图片
*/
RestResp<ImgVerifyCodeRespDto> getImgVerifyCode() throws IOException;
/**
* 图片上传
* @param file 需要上传的图片
* @return 图片访问路径
* */
RestResp<String> uploadImage(MultipartFile file);
}
Service实现类
/**
* 资源(图片/视频/文档)相关服务实现类
*
* @author xiongxiaoyang
* @date 2022/5/17
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class ResourceServiceImpl implements ResourceService {
private final VerifyCodeManager verifyCodeManager;
@Value("${novel.file.upload.path}")
private String fileUploadPath;
@Override
public RestResp<ImgVerifyCodeRespDto> getImgVerifyCode() throws IOException {
String sessionId = IdWorker.get32UUID();
return RestResp.ok(ImgVerifyCodeRespDto.builder()
.sessionId(sessionId)
.img(verifyCodeManager.genImgVerifyCode(sessionId))
.build());
}
@SneakyThrows
@Override
public RestResp<String> uploadImage(MultipartFile file) {
LocalDateTime now = LocalDateTime.now();
String savePath =
SystemConfigConsts.IMAGE_UPLOAD_DIRECTORY
+ now.format(DateTimeFormatter.ofPattern("yyyy")) + File.separator
+ now.format(DateTimeFormatter.ofPattern("MM")) + File.separator
+ now.format(DateTimeFormatter.ofPattern("dd"));
String oriName = file.getOriginalFilename();
assert oriName != null;
String saveFileName = IdWorker.get32UUID() + oriName.substring(oriName.lastIndexOf("."));
File saveFile = new File(fileUploadPath + savePath, saveFileName);
if (!saveFile.getParentFile().exists()) {
boolean isSuccess = saveFile.getParentFile().mkdirs();
if (!isSuccess) {
throw new BusinessException(ErrorCodeEnum.USER_UPLOAD_FILE_ERROR);
}
}
file.transferTo(saveFile);
if (Objects.isNull(ImageIO.read(saveFile))) {
// 上传的文件不是图片
Files.delete(saveFile.toPath());
throw new BusinessException(ErrorCodeEnum.USER_UPLOAD_FILE_TYPE_NOT_MATCH);
}
return RestResp.ok(savePath + File.separator + saveFileName);
}
}
Controller层
/**
* 前台门户-资源(图片/视频/文档)模块 API 控制器
*
* @author xiongxiaoyang
* @date 2022/5/17
*/
@Tag(name = "ResourceController", description = "前台门户-资源模块")
@RestController
@RequestMapping(ApiRouterConsts.API_FRONT_RESOURCE_URL_PREFIX)
@RequiredArgsConstructor
public class ResourceController {
private final ResourceService resourceService;
/**
* 获取图片验证码接口
*/
@Operation(summary = "获取图片验证码接口")
@GetMapping("img_verify_code")
public RestResp<ImgVerifyCodeRespDto> getImgVerifyCode() throws IOException {
return resourceService.getImgVerifyCode();
}
}
借助Hutool工具类
参考https://blog.csdn.net/weixin_47390965/article/details/126763759大佬所写。