package com.code2roc.fastface.controller; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.code2roc.fastface.bizlogic.FaceRegistLogic; import com.code2roc.fastface.model.*; import com.code2roc.fastface.service.IFaceRegistService; import com.code2roc.fastface.util.ConvertOp; import com.code2roc.fastface.util.StringUtil; import lombok.extern.slf4j.Slf4j; import net.coobird.thumbnailator.Thumbnails; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.Base64; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/faceApi") @Slf4j public class FaceRegist2Controller { @Autowired private IFaceRegistService faceRegistService; @Autowired private FaceRegistLogic faceRegistLogic; @Autowired private String localIpAddress; @Value("8081") private String imagesPort; //用于初始化人脸库测试使用 @ResponseBody @PostMapping("/init") public Result2 init(@RequestBody Map params) { log.info("集体注册人脸信息"); long begin = System.currentTimeMillis(); File imageDir = new File(ConvertOp.convert2String(params.get("url"))); for (File file : imageDir.listFiles()) { Path tempDir = Paths.get(System.getProperty("user.dir"), "images"); try { // 确保目录存在 if (!Files.exists(tempDir)) { Files.createDirectories(tempDir); log.info("创建图片目录: {}", tempDir.toAbsolutePath()); } String userCode = file.getName().substring(0, file.getName().lastIndexOf(".")); if (faceRegistService.checkExist("userID", userCode)) { return ResultGenerator.genFailResult("用户ID已注册"); } String base64Image = convert(file, true); RegistResult queryResult = faceRegistLogic.queryFace(base64Image); if (queryResult.getStatus() == 0) { FaceRegistDO faceRegistDO = faceRegistService.selectOneByField("registIndex", queryResult.getMessage()); if (faceRegistDO != null) { return ResultGenerator.genFailResult("用户照片已注册"); } } String userName = "/images/" + userCode + ".jpg"; RegistResult registResult = faceRegistLogic.addFace(userCode, userName, base64Image); if (registResult.getStatus() != 0) { return ResultGenerator.genFailResult(registResult.getMessage()); } Path targetPath = tempDir.resolve(userCode + ".jpg"); Files.copy(file.toPath(), targetPath, StandardCopyOption.REPLACE_EXISTING); JSONObject result = getSuccessResultJson(); result.getJSONObject("data").put("faceUrl", "http://" + localIpAddress + ":" + imagesPort + "/images/" + userCode + ".jpg"); long end = System.currentTimeMillis(); log.info("user[{}],time addFace is:{}", userCode, (end - begin)); return ResultGenerator.genSuccessResult(result); } catch (Exception e) { log.warn("addFace-文件上传失败"); return ResultGenerator.genFailResult(e.getMessage()); } finally { continue; } } return ResultGenerator.genSuccessResult(); } //用于删除人脸库测试使用 @ResponseBody @PostMapping("/deleteAll") @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.READ_UNCOMMITTED) public Object deleteAll(@RequestBody Map params) throws Exception { Result result = Result.okResult(); faceRegistLogic.deleteAllFace(); return result; } @ResponseBody @PostMapping("/list") public Object list(@RequestBody Map params) { Result result = Result.okResult(); HashMap paramMap = new HashMap<>(); String sql = "1=1"; String userName = ConvertOp.convert2String(params.get("userName")); if (!StringUtil.isEmpty(userName)) { sql += " and userName like #{userName}"; paramMap.put("userName", "%" + userName + "%"); } String userID = ConvertOp.convert2String(params.get("userID")); if (!StringUtil.isEmpty(userID)) { sql += " and userID like #{userID}"; paramMap.put("userID", "%" + userID + "%"); } List rows = faceRegistService.selectPageList("userID,userName,registDate,updateDate", sql, "id desc", paramMap); int count = faceRegistService.selectCount(sql, paramMap); result.add("rows", rows); result.add("total", count); return result; } /** * 注册人脸 * * @param image * @param userCode * @return */ @ResponseBody @PostMapping("/addFace") public Result2 insert(@RequestParam("image") MultipartFile image, String userCode) { log.info("注册人脸信息"); long begin = System.currentTimeMillis(); File tempFile = null; Path tempDir = Paths.get(System.getProperty("user.dir"), "images"); String base64Image; try { // 确保目录存在 if (!Files.exists(tempDir)) { Files.createDirectories(tempDir); log.info("创建图片目录: {}", tempDir.toAbsolutePath()); } tempFile = Files.createTempFile(tempDir, "temp-addFace", ".jpg").toFile(); try (InputStream inputStream = image.getInputStream()) { Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); } base64Image = convert(image, true); } catch (Exception e) { // 确保临时文件被删除 if (tempFile != null && tempFile.exists()) { try { if (!tempFile.delete()) { log.warn("Failed to delete temp file: {}", tempFile.getAbsolutePath()); } } catch (Exception e2) { log.warn("Error deleting temp file", e2); } } return ResultGenerator.genFailResult("addFace-文件上传失败"); } try { if (faceRegistService.checkExist("userID", userCode)) { return ResultGenerator.genFailResult("用户ID已注册"); } RegistResult queryResult = faceRegistLogic.queryFace(base64Image); if (queryResult.getStatus() == 0) { FaceRegistDO faceRegistDO = faceRegistService.selectOneByField("registIndex", queryResult.getMessage()); if (faceRegistDO != null) { return ResultGenerator.genFailResult("用户照片已注册"); } } String userName = "/images/" + userCode + ".jpg"; RegistResult registResult = faceRegistLogic.addFace(userCode, userName, base64Image); if (registResult.getStatus() != 0) { return ResultGenerator.genFailResult(registResult.getMessage()); } Path targetPath = tempDir.resolve(userCode + ".jpg"); Files.copy(tempFile.toPath(), targetPath, StandardCopyOption.REPLACE_EXISTING); JSONObject result = getSuccessResultJson(); result.getJSONObject("data").put("faceUrl", "http://" + localIpAddress + ":" + imagesPort + "/images/" + userCode + ".jpg"); long end = System.currentTimeMillis(); log.info("user[{}],time addFace is:{}", userCode, (end - begin)); return ResultGenerator.genSuccessResult(result); } catch (Exception e) { log.warn("addFace-文件上传失败"); return ResultGenerator.genFailResult(e.getMessage()); } finally { // 确保临时文件被删除 if (tempFile != null && tempFile.exists()) { try { if (!tempFile.delete()) { log.warn("Failed to delete temp file: {}", tempFile.getAbsolutePath()); } } catch (Exception e) { log.warn("Error deleting temp file", e); } } } } /** * 获取人脸地址 * * @param userCode * @return */ @ResponseBody @PostMapping("/getUserFaceUrl") public Result2 getUserFaceUrl(String userCode) { log.info("获取人脸信息"); long begin = System.currentTimeMillis(); FaceRegistDO faceRegistDO = faceRegistService.selectOne(userCode); long end = System.currentTimeMillis(); log.info("user[{}],time getUserFaceUrl is:{}", userCode, (end - begin)); if (faceRegistDO == null) { return ResultGenerator.genFailResult("获取不到数据"); } return ResultGenerator.genSuccessResult("http://" + localIpAddress + ":" + imagesPort + "/images/" + userCode + ".jpg"); } @ResponseBody @PostMapping("/identifyFace") public Result2 identifyFace(@RequestParam("image") MultipartFile image) { log.info("人脸识别"); long begin = System.currentTimeMillis(); String base64Image = null; try { base64Image = convert(image, true); long end = System.currentTimeMillis(); log.info("time identifyFace 压缩图片is:{}", (end - begin)); RegistResult registResult = faceRegistLogic.queryFace(base64Image); if (registResult.getStatus() != 0) { return ResultGenerator.genSuccessResult(registResult.getMessage()); } FaceRegistDO faceRegistDO = faceRegistService.selectOneByField("registIndex", registResult.getMessage()); String userId = faceRegistDO.getUserID(); JSONObject result = getSuccessResultJson(); JSONArray jsonArray = new JSONArray(); JSONObject jsonObject = new JSONObject(); jsonArray.add(jsonObject); jsonObject.put("score", 100); jsonObject.put("user_id", userId); result.getJSONObject("data").put("result", jsonArray); end = System.currentTimeMillis(); log.info("user[{}],time identifyFace is:{}", userId, (end - begin)); return ResultGenerator.genSuccessResult(result); } catch (Exception e) { log.warn("identifyFace Exception", e); return ResultGenerator.genSuccessResult(e.getMessage()); } } @ResponseBody @PostMapping("/delFace") public Result2 delFace(String userCode) { String userID = userCode; if (!faceRegistService.checkExist("userID", userID)) { return ResultGenerator.genFailResult("用户ID不存在"); } RegistResult registResult = faceRegistLogic.deleteFace(userID); if (registResult.getStatus() != 0) { return ResultGenerator.genFailResult(""); } return ResultGenerator.genSuccessResult(); } /** * 将MultipartFile转换为Data URL格式的字符串 * * @param image 要转换的MultipartFile * @return 符合Data URL规范的字符串 * @throws Exception 当读取文件或转换过程中发生错误时抛出 */ public static String convert(MultipartFile image, boolean needYs) throws Exception { if (needYs) { byte[] compressedBytes = null; // 1. 图片压缩处理 // 对于小图片(小于1M)直接使用原图,大图片进行压缩 if (image.getSize() > 1024 * 1024) { log.info("图片大小: {}KB,需要压缩", image.getSize() / 1024); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // 使用Thumbnails进行压缩,限制最大尺寸为1920x1080,质量0.7 Thumbnails.of(image.getInputStream()) .size(1920, 1080) // 按比例缩放,最大尺寸限制 .outputFormat("jpg") // 明确输出格式 // .outputQuality(0.80f) // 质量压缩,平衡大小和清晰度 .toOutputStream(outputStream); compressedBytes = outputStream.toByteArray(); log.info("压缩后图片大小: {}KB", compressedBytes.length / 1024); } else { log.info("图片大小: {}KB,无需压缩", image.getSize() / 1024); compressedBytes = image.getBytes(); } return "data:jpg;base64," + Base64.getEncoder().encodeToString(compressedBytes); } else { if (image == null || image.isEmpty()) { throw new IllegalArgumentException("文件不能为空"); } // 获取文件的MIME类型 String contentType = image.getContentType(); if (contentType == null || !contentType.startsWith("image/")) { throw new IllegalArgumentException("不支持的文件类型,必须是图像文件"); } // 读取文件字节并编码为Base64 byte[] fileBytes = image.getBytes(); String base64Encoded = Base64.getEncoder().encodeToString(fileBytes); // 构建Data URL格式的字符串 return "data:" + contentType + ";base64," + base64Encoded; } } public static String convert(File imageFile, boolean needYs) throws Exception { // 验证文件合法性 if (imageFile == null || !imageFile.exists() || !imageFile.isFile()) { throw new IllegalArgumentException("文件不存在或不是有效的文件"); } if (needYs) { byte[] processedBytes = null; long fileSize = imageFile.length(); // 图片压缩处理(大于1M的文件才压缩) if (fileSize > 1024 * 1024) { log.info("图片大小: {}KB,需要压缩", fileSize / 1024); try (InputStream inputStream = new FileInputStream(imageFile); ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { // 使用Thumbnails进行压缩处理 Thumbnails.of(inputStream) .size(1920, 1080) // 按比例缩放,最大尺寸限制 .outputFormat("jpg") // 明确输出格式 // .outputQuality(0.80f) // 质量压缩参数 .toOutputStream(outputStream); processedBytes = outputStream.toByteArray(); log.info("压缩后图片大小: {}KB", processedBytes.length / 1024); } } else { log.info("图片大小: {}KB,无需压缩", fileSize / 1024); // 小文件直接读取字节 processedBytes = Files.readAllBytes(imageFile.toPath()); } // 返回压缩后的Data URL(统一为jpg格式) return "data:image/jpg;base64," + Base64.getEncoder().encodeToString(processedBytes); } else { // 不压缩的情况,保留原始格式 String contentType = Files.probeContentType(imageFile.toPath()); if (contentType == null || !contentType.startsWith("image/")) { throw new IllegalArgumentException("不支持的文件类型,必须是图像文件"); } // 读取文件字节并编码为Base64 byte[] fileBytes = Files.readAllBytes(imageFile.toPath()); String base64Encoded = Base64.getEncoder().encodeToString(fileBytes); // 构建Data URL格式的字符串 return "data:" + contentType + ";base64," + base64Encoded; } } private JSONObject getSuccessResultJson() { JSONObject jsonObject = new JSONObject(); jsonObject.put("error", 0); jsonObject.put("msg", "success"); JSONObject data = new JSONObject(); jsonObject.put("data", data); return jsonObject; } private JSONObject getIdentifyFaceSuccessResultJson() { JSONObject jsonObject = new JSONObject(); jsonObject.put("error", 0); jsonObject.put("msg", "success"); JSONObject data = new JSONObject(); JSONArray resultArray = new JSONArray(); JSONObject resultObject = new JSONObject(); resultArray.add(resultObject); data.put("result", resultArray); jsonObject.put("data", data); return jsonObject; } }