SpringBoot接入DeepSeek-OCR实现结构化数据提取
在企业级应用中,纸质单据、发票、合同等文档的数字化处理一直是一个高频且繁琐的任务。传统的人工录入方式不仅效率低下,还容易出错。随着AI技术的发展,尤其是OCR(光学字符识别)能力的显著提升,自动化提取图像中的文本信息已成为可能。
本文将带你一步步实现SpringBoot应用接入DeepSeek-OCR-WEBUI服务,重点解决一个典型业务场景:从包含表格的图片中自动识别并提取结构化数据,最终转换为JSON格式供系统使用。整个过程涵盖后端部署、接口调用、HTML解析、前后端集成与Docker打包发布,是一套可直接落地的企业级解决方案。
1. 部署DeepSeek-OCR-WEBUI服务
要让SpringBoot应用能够调用OCR功能,首先需要确保DeepSeek-OCR-WEBUI服务已经正确部署并运行。该镜像基于DeepSeek开源的大模型,具备强大的文本检测与识别能力,尤其擅长处理复杂布局的文档内容,如表格、公式、多语言混合文本等。
1.1 启动OCR服务
进入项目目录并使用Docker Compose启动服务:
cd ~/DeepSeek-OCR-WebUI docker compose up -d启动完成后,查看日志确认服务是否正常运行:
docker logs -f deepseek-ocr-webui当看到类似Uvicorn running on http://0.0.0.0:8080的输出时,说明Web API服务已就绪。
1.2 OCR核心接口说明
服务暴露了一个关键POST接口用于图像识别:
POST /ocr支持以下参数:
| 参数名 | 类型 | 可选值 | 说明 |
|---|---|---|---|
| file | File | - | 要上传的图像文件 |
| prompt_type | string | document,ocr,free,figure,describe,find,freeform | 指定识别模式 |
| find_term | string | 自定义关键词 | 仅在find模式下有效 |
| custom_prompt | string | - | 自定义提示词 |
| grounding | boolean | true / false | 是否启用分组 |
本案例重点使用
prompt_type=figure,因为该模式专为图表和表格设计,能更准确地保留原始结构,输出HTML格式的表格代码。
例如,传入一张采购单截图,设置prompt_type=figure,返回结果将是标准的<table>HTML片段,便于后续结构化解析。
2. SpringBoot项目结构设计
我们的目标是构建一个轻量但完整的Web应用,接收用户上传的含表格图片,调用OCR服务完成识别,并将结果以JSON形式返回给前端展示或入库。
2.1 技术栈选择
- 后端框架:Spring Boot 3.x
- HTTP客户端:RestTemplate(简化远程调用)
- JSON处理:Fastjson
- 前端:Vue3 + Element Plus(静态页面嵌入)
- 打包部署:Docker + docker-compose
2.2 核心模块划分
| 模块 | 功能描述 |
|---|---|
| OcrService | 定义OCR识别接口 |
| DeepSeekOcrService | 实现类,封装对OCR服务的HTTP调用 |
| OcrController | 提供REST API供前端调用 |
| HtmlParser | 将OCR返回的HTML表格转为List |
| Vue UI | 用户操作界面,支持图片上传与结果显示 |
3. 实现OCR服务调用逻辑
3.1 定义服务接口
// src/main/java/com/kaifamiao/dswebui/service/OcrService.java public interface OcrService { /** * 识别表格图片并返回结构化数据 * * @param file 上传的包含表格的图片文件 * @return 包含表格数据的Map对象,将以JSON格式返回给前端 */ Map<String, Object> recognitionTable(MultipartFile file); }3.2 实现远程调用与HTML解析
// src/main/java/com/kaifamiao/dswebui/service/DeepSeekOcrService.java @Service @Slf4j public class DeepSeekOcrService implements OcrService { private static final String OCR_SERVICE_URL = "http://localhost:8080/ocr"; @Override public Map<String, Object> recognitionTable(MultipartFile file) { log.info("开始处理文件: {}", file.getOriginalFilename()); try { RestTemplate restTemplate = new RestTemplate(); // 准备文件资源 ByteArrayResource resource = new ByteArrayResource(file.getBytes()) { @Override public String getFilename() { return file.getOriginalFilename(); } }; // 构建请求参数 MultiValueMap<String, Object> body = new LinkedMultiValueMap<>(); body.add("file", resource); body.add("prompt_type", "figure"); // 关键:使用图表模式识别表格 // 设置请求头 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); // 创建请求实体 HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers); // 发送POST请求 ResponseEntity<String> response = restTemplate.postForEntity(OCR_SERVICE_URL, requestEntity, String.class); if (response.getStatusCode().is2xxSuccessful()) { String htmlContent = response.getBody(); log.info("OCR返回HTML长度: {}", htmlContent.length()); return parseHtmlTableToJSON(htmlContent); } else { log.error("OCR服务调用失败,状态码: {}", response.getStatusCode()); throw new RuntimeException("OCR识别失败"); } } catch (Exception e) { log.error("OCR识别过程中发生异常", e); throw new RuntimeException("文件处理失败: " + e.getMessage(), e); } } /** * 将HTML表格解析为JSON格式 * * @param html 包含<table>标签的HTML字符串 * @return 转换后的JSON数据,包含表头和行数据 */ private Map<String, Object> parseHtmlTableToJSON(String html) { Document doc = Jsoup.parse(html); Element table = doc.selectFirst("table"); if (table == null) return Collections.emptyMap(); List<Element> rows = table.select("tr"); if (rows.isEmpty()) return Collections.emptyMap(); // 提取表头 List<String> headers = new ArrayList<>(); Elements headerCells = rows.get(0).select("td, th"); for (Element cell : headerCells) { headers.add(cell.text().trim()); } // 提取数据行 List<Map<String, String>> dataRows = new ArrayList<>(); for (int i = 1; i < rows.size(); i++) { Elements cells = rows.get(i).select("td"); Map<String, String> row = new HashMap<>(); for (int j = 0; j < Math.min(cells.size(), headers.size()); j++) { String key = headers.get(j); String value = cells.get(j).text().trim(); row.put(key, value.isEmpty() ? "" : value); } dataRows.add(row); } // 组装返回结果 Map<String, Object> result = new HashMap<>(); result.put("headers", headers); result.put("data", dataRows); result.put("totalRows", dataRows.size()); return result; } }依赖说明
需引入以下Maven依赖:
<!-- JSoup用于HTML解析 --> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.16.1</version> </dependency> <!-- Fastjson用于JSON序列化 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency>4. 编写控制器暴露API
// src/main/java/com/kaifamiao/dswebui/controller/OcrController.java @RestController @RequestMapping("/api/ocr") @Slf4j public class OcrController { @Autowired private OcrService ocrService; @PostMapping("/process") public Map<String, Object> processFile(@RequestParam("file") MultipartFile file) { if (file.isEmpty()) { throw new IllegalArgumentException("上传文件不能为空"); } long startTime = System.currentTimeMillis(); Map<String, Object> result = ocrService.recognitionTable(file); long costTime = System.currentTimeMillis() - startTime; result.put("success", true); result.put("costTimeMs", costTime); log.info("OCR识别完成,耗时{}ms", costTime); return result; } }该接口接收名为file的图片上传请求,调用服务层处理后返回结构化数据,包含字段名、每行数据及统计信息。
5. 添加测试用例验证功能
为了确保服务稳定性,编写单元测试验证全流程可用性。
// src/test/java/com/kaifamiao/dswebui/service/OcrServiceTest.java @SpringBootTest @Slf4j public class OcrServiceTest { @Autowired private OcrService ocrService; @Test void testRecognitionTableSuccess() throws Exception { ClassPathResource resource = new ClassPathResource("voucher.jpg"); MockMultipartFile file = new MockMultipartFile( "file", "voucher.jpg", "image/jpeg", resource.getInputStream() ); Map<String, Object> result = ocrService.recognitionTable(file); log.info("OCR识别结果: \n{}", JSON.toJSONString(result, true)); Assertions.assertNotNull(result.get("data")); Assertions.assertTrue(((List<?>) result.get("data")).size() > 0); } }运行测试,观察日志输出是否成功解析出表格内容。
6. 前端页面集成(Vue3)
前端采用Vue3 + Element Plus搭建简易交互界面,主要功能包括:
- 图片上传区域
- 实时预览
- 表格数据显示
- 错误提示
6.1 页面结构示例
<template> <div class="container"> <h2>表格图片OCR识别</h2> <el-upload action="/api/ocr/process" :auto-upload="true" :show-file-list="false" accept="image/*" :on-success="handleSuccess" :on-error="handleError"> <img v-if="imageUrl" :src="imageUrl" class="preview-img"/> <el-button>点击上传表格图片</el-button> </el-upload> <div v-if="tableData.length > 0" class="result-table"> <h3>识别结果</h3> <el-table :data="tableData" border style="width: 100%"> <el-table-column v-for="col in columns" :key="col" :prop="col" :label="col" /> </el-table> </div> </div> </template>6.2 构建与集成
进入前端目录执行构建:
npm install npm run build将生成的dist文件夹内容复制到SpringBoot项目的src/main/resources/static目录下,即可通过内置Tomcat直接访问UI。
7. Docker打包与一键部署
完成开发后,我们将整个应用容器化,便于部署到生产环境。
7.1 编写Dockerfile
FROM openjdk:21-jdk-slim WORKDIR /app COPY target/deepseek-web-ui.jar /app/app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]7.2 编排服务(docker-compose.yml)
version: '3.8' services: ocr-app: build: . ports: - "8080:8080" environment: - SERVER_PORT=8080 volumes: - ./logs:/app/logs7.3 构建并启动
mvn clean package -DskipTests docker compose up -d --build访问http://localhost:8080即可打开OCR识别页面,上传图片即可实时获取结构化数据。
8. 总结
本文完整演示了如何将DeepSeek-OCR-WEBUI这一高性能OCR引擎集成进SpringBoot应用,实现了从“图片→HTML表格→JSON结构化数据”的全链路自动化提取流程。这套方案具有以下优势:
- 高精度识别:利用DeepSeek大模型的强大能力,准确还原复杂表格结构;
- 低耦合设计:通过HTTP接口调用,OCR服务可独立升级维护;
- 易扩展性强:支持多种
prompt_type,未来可拓展至合同解析、发票识别等场景; - 快速落地:提供完整前后端代码与Docker部署脚本,开箱即用。
无论是财务报销、物流运单、库存管理还是教育阅卷,只要涉及纸质表格电子化,都可以基于此方案进行定制开发,大幅提升数据录入效率,降低人工成本。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。