package com.ruoyi.system.controller;
|
|
import com.alibaba.fastjson.JSONObject;
|
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.utils.file.FileUploadUtils;
|
import com.ruoyi.system.utils.AliOCRUtil;
|
import com.ruoyi.system.utils.BaiduOCRUtil;
|
import com.ruoyi.system.utils.TencentOCRUtil;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.multipart.MultipartFile;
|
|
import java.io.File;
|
import java.util.Arrays;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.stream.Collectors;
|
|
/**
|
* OCR识别Controller
|
* 支持阿里云OCR和百度OCR服务
|
* @author ruoyi
|
*/
|
@RestController
|
@RequestMapping("/system/ocr")
|
public class OCRController extends BaseController {
|
|
@Autowired
|
private AliOCRUtil aliOCRUtil;
|
|
// 支持的OCR识别类型
|
private static final List<String> SUPPORTED_TYPES = Arrays.asList("General", "Invoice", "IdCard", "HandWriting");
|
|
/**
|
* 上传图片并进行OCR识别
|
* @param file 上传的图片文件
|
* @param type 识别类型(General-通用, Invoice-发票, IdCard-身份证, HandWriting-手写体)
|
* @param provider OCR服务提供商(ali-阿里云, baidu-百度)
|
* @return OCR识别结果
|
*/
|
@PostMapping(value = "/recognize", consumes = "multipart/form-data")
|
public AjaxResult recognizeImage(@RequestParam("file") MultipartFile file,
|
@RequestParam(value = "type", defaultValue = "General") String type,
|
@RequestParam(value = "provider", defaultValue = "ali") String provider,
|
@RequestParam(value = "itemNames", required = false) String[] itemNames) {
|
try {
|
if (file.isEmpty()) {
|
return error("上传图片不能为空");
|
}
|
|
// 验证识别类型
|
if (!SUPPORTED_TYPES.contains(type)) {
|
return error("不支持的识别类型: " + type + ", 支持的类型: " + String.join(",", SUPPORTED_TYPES));
|
}
|
|
// 保存临时文件
|
String tempDir = System.getProperty("java.io.tmpdir");
|
String originalFilename = file.getOriginalFilename();
|
File tempFile = new File(tempDir, System.currentTimeMillis() + "_" + originalFilename);
|
file.transferTo(tempFile);
|
|
// 根据提供商调用不同的OCR服务
|
JSONObject ocrResult;
|
if ("baidu".equalsIgnoreCase(provider)) {
|
// 百度OCR只支持部分类型
|
if ("General".equals(type)) {
|
ocrResult = BaiduOCRUtil.generalRecognize(tempFile);
|
} else if ("HandWriting".equals(type)) {
|
ocrResult = BaiduOCRUtil.handwritingRecognize(tempFile);
|
} else {
|
ocrResult = BaiduOCRUtil.generalRecognize(tempFile); // 默认使用通用识别
|
}
|
} else if ("tencent".equalsIgnoreCase(provider)) {
|
// 腾讯云OCR只支持部分类型
|
if ("General".equals(type)) {
|
ocrResult = TencentOCRUtil.generalRecognize(tempFile);
|
} else if ("HandWriting".equals(type)) {
|
ocrResult = TencentOCRUtil.handwritingRecognize(tempFile.getAbsolutePath(), itemNames);
|
} else {
|
ocrResult = TencentOCRUtil.generalRecognize(tempFile); // 默认使用通用识别
|
}
|
} else {
|
// 阿里云OCR
|
ocrResult = AliOCRUtil.recognizeTextByFile(tempFile, type);
|
}
|
|
// 删除临时文件
|
tempFile.delete();
|
|
// 构建返回结果
|
Map<String, Object> result = new HashMap<>();
|
result.put("ocrResult", ocrResult);
|
result.put("fileName", originalFilename);
|
result.put("type", type);
|
result.put("provider", provider);
|
|
if (ocrResult.getBooleanValue("success")) {
|
return success(result);
|
} else {
|
return error("OCR识别失败: " + ocrResult.getString("error"));
|
}
|
|
} catch (Exception e) {
|
logger.error("OCR识别异常", e);
|
return error("OCR识别异常: " + e.getMessage());
|
}
|
}
|
|
/**
|
* 通过图片URL进行OCR识别
|
* @param imageUrl 图片URL地址
|
* @param type 识别类型(General-通用, Invoice-发票, IdCard-身份证, HandWriting-手写体)
|
* @param provider OCR服务提供商(ali-阿里云, baidu-百度)
|
* @return OCR识别结果
|
*/
|
@GetMapping("/recognizeByUrl")
|
public AjaxResult recognizeByUrl(@RequestParam("imageUrl") String imageUrl,
|
@RequestParam(value = "type", defaultValue = "General") String type,
|
@RequestParam(value = "provider", defaultValue = "ali") String provider,
|
@RequestParam(value = "itemNames", required = false) String[] itemNames) {
|
try {
|
// 验证识别类型
|
if (!SUPPORTED_TYPES.contains(type)) {
|
return error("不支持的识别类型: " + type + ", 支持的类型: " + String.join(",", SUPPORTED_TYPES));
|
}
|
|
// 根据提供商调用不同的OCR服务
|
JSONObject ocrResult;
|
if ("baidu".equalsIgnoreCase(provider)) {
|
// 百度OCR只支持部分类型
|
if ("General".equals(type)) {
|
ocrResult = BaiduOCRUtil.generalRecognize(imageUrl);
|
} else if ("HandWriting".equals(type)) {
|
ocrResult = BaiduOCRUtil.handwritingRecognize(imageUrl);
|
} else {
|
ocrResult = BaiduOCRUtil.generalRecognize(imageUrl); // 默认使用通用识别
|
}
|
} else if ("tencent".equalsIgnoreCase(provider)) {
|
// 腾讯云OCR只支持部分类型
|
if ("General".equals(type)) {
|
ocrResult = TencentOCRUtil.generalRecognize(imageUrl);
|
} else if ("HandWriting".equals(type)) {
|
ocrResult = TencentOCRUtil.handwritingRecognize(imageUrl, itemNames);
|
} else {
|
ocrResult = TencentOCRUtil.generalRecognize(imageUrl); // 默认使用通用识别
|
}
|
} else {
|
// 阿里云OCR
|
ocrResult = AliOCRUtil.recognizeTextByUrl(imageUrl, type);
|
}
|
|
// 构建返回结果
|
Map<String, Object> result = new HashMap<>();
|
result.put("ocrResult", ocrResult);
|
result.put("imageUrl", imageUrl);
|
result.put("type", type);
|
result.put("provider", provider);
|
|
if (ocrResult.getBooleanValue("success")) {
|
return success(result);
|
} else {
|
return error("OCR识别失败: " + ocrResult.getString("error"));
|
}
|
|
} catch (Exception e) {
|
logger.error("OCR识别异常", e);
|
return error("OCR识别异常: " + e.getMessage());
|
}
|
}
|
|
/**
|
* 获取支持的OCR识别类型列表
|
* @return 识别类型列表
|
*/
|
@GetMapping("/types")
|
public AjaxResult getSupportedTypes() {
|
Map<String, Object> result = new HashMap<>();
|
result.put("types", SUPPORTED_TYPES);
|
|
List<Map<String, String>> typeList = SUPPORTED_TYPES.stream().map(type -> {
|
Map<String, String> typeInfo = new HashMap<>();
|
typeInfo.put("value", type);
|
|
// 根据类型设置显示名称
|
switch (type) {
|
case "General":
|
typeInfo.put("label", "通用文字识别");
|
break;
|
case "Invoice":
|
typeInfo.put("label", "发票识别");
|
break;
|
case "IdCard":
|
typeInfo.put("label", "身份证识别");
|
break;
|
case "HandWriting":
|
typeInfo.put("label", "手写体识别");
|
break;
|
default:
|
typeInfo.put("label", type);
|
break;
|
}
|
return typeInfo;
|
}).collect(Collectors.toList());
|
|
result.put("typeList", typeList);
|
return success(result);
|
}
|
|
/**
|
* 获取支持的OCR服务提供商列表
|
* @return OCR服务提供商列表
|
*/
|
@GetMapping("/providers")
|
public AjaxResult getSupportedProviders() {
|
Map<String, Object> result = new HashMap<>();
|
List<Map<String, String>> providerList = Arrays.asList(
|
createProviderInfo("ali", "阿里云OCR", true),
|
createProviderInfo("baidu", "百度OCR", true),
|
createProviderInfo("tencent", "腾讯云OCR", true)
|
);
|
result.put("providers", providerList);
|
return success(result);
|
}
|
|
/**
|
* 提取OCR结果中的目标字段
|
* @param ocrResult OCR原始结果
|
* @return 提取的字段信息
|
*/
|
@PostMapping("/extractFields")
|
public AjaxResult extractFields(@RequestBody JSONObject ocrResult) {
|
try {
|
// 检查是否为百度OCR结果
|
String provider = ocrResult.getString("provider");
|
Map<String, String> extracted;
|
if ("baidu".equalsIgnoreCase(provider)) {
|
extracted = BaiduOCRUtil.extractTargetFields(ocrResult);
|
} else if ("tencent".equalsIgnoreCase(provider)) {
|
extracted = TencentOCRUtil.extractTargetFields(ocrResult);
|
} else {
|
extracted = AliOCRUtil.extractTargetFields(ocrResult);
|
}
|
return success(extracted);
|
} catch (Exception e) {
|
logger.error("字段提取异常", e);
|
return error("字段提取异常: " + e.getMessage());
|
}
|
}
|
|
/**
|
* 腾讯云手写体识别(支持自定义字段提取)
|
* @param file 上传的图片文件
|
* @param itemNames 需要提取的字段名称数组
|
* @return 识别结果 Map,key为字段名,value为识别内容
|
*/
|
@PostMapping(value = "/tencent/handwriting", consumes = "multipart/form-data")
|
public AjaxResult tencentHandwritingRecognize(@RequestParam("file") MultipartFile file,
|
@RequestParam(value = "itemNames", required = false) String[] itemNames) {
|
try {
|
if (file.isEmpty()) {
|
return error("上传图片不能为空");
|
}
|
|
// 保存临时文件
|
String tempDir = System.getProperty("java.io.tmpdir");
|
String originalFilename = file.getOriginalFilename();
|
File tempFile = new File(tempDir, System.currentTimeMillis() + "_" + originalFilename);
|
file.transferTo(tempFile);
|
|
// 调用腾讯云手写体识别
|
Map<String, String> resultMap = TencentOCRUtil.handwritingRecognizeWith(tempFile.getAbsolutePath(), itemNames);
|
|
// 删除临时文件
|
tempFile.delete();
|
|
// 检查是否有错误
|
if (resultMap.containsKey("error")) {
|
return error("腾讯云OCR手写体识别失败: " + resultMap.get("error"));
|
}
|
|
// 构建返回结果
|
Map<String, Object> result = new HashMap<>();
|
result.put("fileName", originalFilename);
|
result.put("type", "HandWriting");
|
result.put("provider", "tencent");
|
result.put("fields", resultMap);
|
result.put("fieldCount", resultMap.size());
|
|
return success(result);
|
|
} catch (Exception e) {
|
logger.error("腾讯云OCR手写体识别异常", e);
|
return error("腾讯云OCR手写体识别异常: " + e.getMessage());
|
}
|
}
|
|
/**
|
* 腾讯云手写体识别通过URL(支持自定义字段提取)
|
* @param imageUrl 图片URL地址
|
* @param itemNames 需要提取的字段名称数组
|
* @return 识别结果 Map,key为字段名,value为识别内容
|
*/
|
@GetMapping("/tencent/handwritingByUrl")
|
public AjaxResult tencentHandwritingRecognizeByUrl(@RequestParam("imageUrl") String imageUrl,
|
@RequestParam(value = "itemNames", required = false) String[] itemNames) {
|
try {
|
// 调用腾讯云手写体识别
|
Map<String, String> resultMap = TencentOCRUtil.handwritingRecognizeWith(imageUrl, itemNames);
|
|
// 检查是否有错误
|
if (resultMap.containsKey("error")) {
|
return error("腾讯云OCR手写体识别失败: " + resultMap.get("error"));
|
}
|
|
// 构建返回结果
|
Map<String, Object> result = new HashMap<>();
|
result.put("imageUrl", imageUrl);
|
result.put("type", "HandWriting");
|
result.put("provider", "tencent");
|
result.put("fields", resultMap);
|
result.put("fieldCount", resultMap.size());
|
|
return success(result);
|
|
} catch (Exception e) {
|
logger.error("腾讯云OCR手写体识别异常", e);
|
return error("腾讯云OCR手写体识别异常: " + e.getMessage());
|
}
|
}
|
|
/**
|
* 腾讯云手写体识别(支持多图片批量识别)
|
* @param files 上传的图片文件数组
|
* @param itemNames 需要提取的字段名称数组
|
* @return 识别结果,合并所有图片的识别字段
|
*/
|
@PostMapping(value = "/tencent/handwriting/batch", consumes = "multipart/form-data")
|
public AjaxResult tencentHandwritingRecognizeBatch(@RequestParam("files") MultipartFile[] files,
|
@RequestParam(value = "itemNames", required = false) String[] itemNames) {
|
try {
|
if (files == null || files.length == 0) {
|
return error("上传图片不能为空");
|
}
|
|
// 合并所有图片的识别结果
|
Map<String, String> mergedResultMap = new HashMap<>();
|
int successCount = 0;
|
int failCount = 0;
|
StringBuilder errorMessages = new StringBuilder();
|
|
for (MultipartFile file : files) {
|
if (file.isEmpty()) {
|
continue;
|
}
|
|
try {
|
// 保存临时文件
|
String tempDir = System.getProperty("java.io.tmpdir");
|
String originalFilename = file.getOriginalFilename();
|
File tempFile = new File(tempDir, System.currentTimeMillis() + "_" + originalFilename);
|
file.transferTo(tempFile);
|
|
// 调用腾讯云手写体识别
|
Map<String, String> resultMap = TencentOCRUtil.handwritingRecognizeWith(tempFile.getAbsolutePath(), itemNames);
|
|
// 删除临时文件
|
tempFile.delete();
|
|
// 检查是否有错误
|
if (resultMap.containsKey("error")) {
|
failCount++;
|
errorMessages.append(originalFilename).append(":").append(resultMap.get("error")).append("; ");
|
logger.warn("图片 {} 识别失败: {}", originalFilename, resultMap.get("error"));
|
} else {
|
// 合并识别结果(如果key已存在,不覆盖)
|
for (Map.Entry<String, String> entry : resultMap.entrySet()) {
|
if (!mergedResultMap.containsKey(entry.getKey()) || mergedResultMap.get(entry.getKey()).isEmpty()) {
|
mergedResultMap.put(entry.getKey(), entry.getValue());
|
}
|
}
|
successCount++;
|
logger.info("图片 {} 识别成功,提取 {} 个字段", originalFilename, resultMap.size());
|
}
|
} catch (Exception e) {
|
failCount++;
|
errorMessages.append(file.getOriginalFilename()).append(":").append(e.getMessage()).append("; ");
|
logger.error("处理图片 {} 时发生异常", file.getOriginalFilename(), e);
|
}
|
}
|
|
// 构建返回结果
|
Map<String, Object> result = new HashMap<>();
|
result.put("type", "HandWriting");
|
result.put("provider", "tencent");
|
result.put("fields", mergedResultMap);
|
result.put("fieldCount", mergedResultMap.size());
|
result.put("totalImages", files.length);
|
result.put("successCount", successCount);
|
result.put("failCount", failCount);
|
|
if (failCount > 0) {
|
result.put("errors", errorMessages.toString());
|
}
|
|
if (successCount == 0) {
|
return error("所有图片识别失败: " + errorMessages.toString());
|
}
|
|
return success(result);
|
|
} catch (Exception e) {
|
logger.error("腾讯云OCR手写体批量识别异常", e);
|
return error("腾讯云OCR手写体批量识别异常: " + e.getMessage());
|
}
|
}
|
|
/**
|
* 创建服务提供商信息
|
* @param value 服务提供商标识
|
* @param label 服务提供商显示名称
|
* @param available 是否可用
|
* @return 服务提供商信息
|
*/
|
private Map<String, String> createProviderInfo(String value, String label, boolean available) {
|
Map<String, String> providerInfo = new HashMap<>();
|
providerInfo.put("value", value);
|
providerInfo.put("label", label);
|
providerInfo.put("available", String.valueOf(available));
|
return providerInfo;
|
}
|
}
|