发布于2026-07-24 阅读(0)
扫一扫,手机访问
说起AI目标检测,YOLO系列模型绝对是绕不开的存在——高效、精准,堪称领域内的标杆。而Spring Boot作为Java生态里最流行的Web开发框架,天然适合把YOLO的检测能力封装成可复用的接口。这篇文章会把Windows环境下,通过Docker部署YOLOv8并集成到Spring Boot项目的完整流程走一遍,从环境准备、问题排查到代码集成、接口测试,每个环节都给出实操步骤和解决方案。适合有一定Java和Docker基础的开发者阅读。

在本地部署YOLO模型时,环境依赖复杂、版本冲突、跨平台兼容性差这些头疼的问题几乎人人都会遇到。而Docker恰好能一揽子解决——通过容器化封装YOLO运行所需的所有依赖,实现“一次构建,到处运行”。
再结合Spring Boot的优势,我们可以把YOLO的图片检测能力封装成HTTP接口,支持前端上传图片、后端返回检测结果,轻松集成到小程序、APP、管理系统等各类业务场景中。说白了,就是让检测能力接口化,业务集成轻量化。
本文核心目标:在Windows环境下,完成Docker部署YOLOv8 → 解决部署过程中的常见问题 → 将YOLO检测能力集成到Spring Boot项目 → 提供可直接调用的图片检测接口。
安装Docker Desktop后,开启WSL2后端(性能优于Hyper-V):
配置文件共享(避免挂载目录权限问题):
D:yolo_test(用于挂载Docker容器,存储图片和检测结果),保存后重启Docker本文基于已有的Spring Boot项目(原有功能包含Ollama大模型集成、PGvector向量存储、阿里云语音识别等),核心是在原有项目基础上新增YOLO图片检测模块,无需从零搭建Spring Boot项目。
这一步是核心基础。我们将通过Docker拉取YOLOv8官方镜像,运行容器并完成图片检测,同时解决部署过程中最常见的“文件找不到”“挂载失败”等问题。
打开PowerShell(管理员模式),执行以下命令拉取官方镜像(内置YOLOv8所有依赖,无需手动配置):
docker pull ultralytics/ultralytics:latest
镜像拉取完成后,执行 docker images 可查看镜像是否存在。
核心命令(挂载本地 D:yolo_test 到容器内 /yolo_data,实现本地与容器文件互通):
docker run -it --rm -v /d/yolo_test:/yolo_data ultralytics/ultralytics:latest
命令说明:
-it:交互式终端,方便在容器内执行命令--rm:容器退出后自动删除,避免残留-v /d/yolo_test:/yolo_data:目录挂载(Windows路径需用 /d/ 替代 D:,Docker才能识别)在本地 D:yolo_test 目录下,放入一张测试图片,命名为 test.jpg(注意:文件名和后缀必须完全匹配,避免大小写错误,如 Test.jpg、test.jpeg 都会导致失败)。
在容器内(命令行显示 root@xxx:/ultralytics#),执行以下命令:
yolo detect predict model=yolov8n.pt source=/yolo_data/test.jpg save=True
命令说明:
model=yolov8n.pt:使用YOLOv8轻量版模型(n=纳米版,适合CPU运行,体积小、速度快)source=/yolo_data/test.jpg:检测源为容器内挂载目录下的test.jpgsave=True:保存检测结果(带检测框的图片)首次运行大概率会遇到以下问题,这里把排查过程完整还原,帮你快速解决。
报错原因:容器内找不到指定图片,核心是两个原因之一:
D:yolo_test 目录下没有test.jpg,或文件名/后缀错误/yolo_data 目录为空解决步骤:
先在容器内执行 ls /yolo_data,查看挂载目录是否有文件:
/d/yolo_test,而非 D:yolo_test)yolo_data(子目录):说明图片放在了 D:yolo_testyolo_data 下,需修改命令为 source=/yolo_data/yolo_data/test.jpg确认本地 D:yolo_test 下有test.jpg,重新执行检测命令
报错现象:检测成功,但本地 D:yolo_test 目录下没有检测结果。
原因:YOLO默认将结果保存到容器内 /ultralytics/runs/detect/predict,而非挂载目录。
解决方法:
手动复制结果到挂载目录(容器内执行):
cp -r /ultralytics/runs/detect/predict /yolo_data/
下次检测直接指定保存路径(推荐),修改命令为:
yolo detect predict model=yolov8n.pt source=/yolo_data/test.jpg save=True project=/yolo_data/runs name=detect
这样结果会直接保存到 /yolo_data/runs/detect,对应本地 D:yolo_testrunsdetect
检测成功后,容器内会输出类似以下日志:
image 1/1 /yolo_data/test.jpg: 640x480 1 cup, 1 tv, 1 mouse, 1 refrigerator, 1 book, 63.4ms Speed: 4.6ms preprocess, 63.4ms inference, 8.6ms postprocess per image at shape (1, 3, 640, 480) Results saved to /yolo_data/runs/detect
此时打开本地 D:yolo_testrunsdetect 目录,会看到带检测框的 test.jpg,图片上会标注出识别到的物体(如杯子、电视、鼠标等),说明Docker部署YOLOv8成功!
Docker部署YOLOv8成功后,接下来集成到Spring Boot项目,提供HTTP接口,支持前端上传图片、后端返回检测结果(识别的物体、数量、耗时、结果图片路径)。
在原有pom.xml基础上,新增文件操作、命令执行相关依赖(用于处理图片上传、调用Docker命令):
2.15.1 3.14.0 commons-io commons-io ${commons-io.version} org.apache.commons commons-lang3 ${commons-lang3.version} ch.ethz.ganymed ganymed-ssh2 262
注:原有依赖(Spring Boot Web、Ollama、PGvector、阿里云语音等)全部保留,无需修改。
在原有配置基础上,新增YOLO和Docker相关配置,同时补充文件上传限制:
# 新增 YOLO 配置
yolo:
# Docker 本地挂载目录(对应本地 D:yolo_test)
docker-mount-path: D:yolo_test
# 容器内挂载路径
container-mount-path: /yolo_data
# YOLO 模型名称(默认 yolov8n.pt)
model-name: yolov8n.pt
# 检测结果保存目录(容器内)
container-result-path: /yolo_data/runs/detect
# 本地临时文件存储目录
local-temp-path: D:yolo_testtemp
# Docker 镜像名称
docker-image: ultralytics/ultralytics:latest
# 原有配置保留,补充文件上传配置
spring:
servlet:
multipart:
max-file-size: 10MB # 单文件最大10MB
max-request-size: 50MB # 总请求最大50MB
新增4个核心类,实现图片上传、Docker命令调用、检测结果解析、接口提供,全部放在 com.ruoyi 包下(与原有项目包结构一致)。
package com.ruoyi.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* YOLO 配置类,读取 application.yml 中的 yolo 相关配置
*/
@Data
@Component
@ConfigurationProperties(prefix = "yolo")
public class YoloConfig {
/**
* Docker 本地挂载目录
*/
private String dockerMountPath;
/**
* 容器内挂载路径
*/
private String containerMountPath;
/**
* YOLO 模型名称
*/
private String modelName;
/**
* 容器内检测结果保存路径
*/
private String containerResultPath;
/**
* 本地临时文件存储目录
*/
private String localTempPath;
/**
* Docker 镜像名称
*/
private String dockerImage;
}
package com.ruoyi.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* YOLO 检测结果返回实体,用于接口返回 JSON 格式数据
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class YoloDetectResult {
/**
* 检测是否成功
*/
private boolean success;
/**
* 错误信息(失败时返回)
*/
private String errorMsg;
/**
* 检测到的目标列表
*/
private List detectObjects;
/**
* 检测耗时(毫秒)
*/
private long costTime;
/**
* 检测结果图片路径(本地)
*/
private String resultImagePath;
/**
* 检测目标实体(单个物体的信息)
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class DetectObject {
/**
* 目标类别(如 cup、tv、mouse)
*/
private String className;
/**
* 目标数量
*/
private int count;
/**
* 置信度(0-1),越大越精准
*/
private float confidence;
}
}
package com.ruoyi.util;
import com.ruoyi.config.YoloConfig;
import com.ruoyi.entity.YoloDetectResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* YOLO 图片检测工具类,封装 Docker 命令调用、结果解析逻辑
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class YoloDetectUtil {
private final YoloConfig yoloConfig;
// 匹配检测结果的正则表达式(如:1 cup, 1 tv, 1 mouse)
private static final Pattern DETECT_PATTERN = Pattern.compile("(\d+)\s+([a-zA-Z_]+)");
/**
* 上传图片并执行 YOLO 检测
* @param file 上传的图片文件
* @return 检测结果(包含识别的物体、耗时、结果路径)
*/
public YoloDetectResult detectImage(MultipartFile file) {
long startTime = System.currentTimeMillis();
try {
// 1. 校验文件是否为空
if (file.isEmpty()) {
return YoloDetectResult.builder()
.success(false)
.errorMsg("上传的图片文件为空")
.build();
}
// 2. 校验图片格式(仅支持 jpg/jpeg/png/bmp)
String originalFilename = file.getOriginalFilename();
if (originalFilename == null || !originalFilename.matches(".*\.(jpg|jpeg|png|bmp)$")) {
return YoloDetectResult.builder()
.success(false)
.errorMsg("仅支持 jpg/jpeg/png/bmp 格式的图片")
.build();
}
// 3. 创建临时目录(用于存储上传的图片)
File tempDir = new File(yoloConfig.getLocalTempPath());
if (!tempDir.exists()) {
boolean mkdirs = tempDir.mkdirs();
if (!mkdirs) {
log.error("创建临时目录失败:{}", yoloConfig.getLocalTempPath());
return YoloDetectResult.builder()
.success(false)
.errorMsg("创建临时目录失败")
.build();
}
}
// 4. 保存上传的图片到本地挂载目录(Docker 可访问)
String fileName = UUID.randomUUID() + "_" + originalFilename;
String localImagePath = yoloConfig.getDockerMountPath() + File.separator + fileName;
File localImageFile = new File(localImagePath);
file.transferTo(localImageFile);
log.info("上传的图片已保存到:{}", localImagePath);
// 5. 构建 Docker 命令(调用 YOLO 检测)
String containerImagePath = yoloConfig.getContainerMountPath() + "/" + fileName;
String dockerCmd = String.format(
"docker run --rm -v %s:%s %s yolo detect predict model=%s source=%s save=True project=%s name=detect",
yoloConfig.getDockerMountPath().replace("\", "/"), // Windows 路径转换为 Docker 可识别格式
yoloConfig.getContainerMountPath(),
yoloConfig.getDockerImage(),
yoloConfig.getModelName(),
containerImagePath,
yoloConfig.getContainerResultPath()
);
log.info("执行 Docker 命令:{}", dockerCmd);
// 6. 执行 Docker 命令,获取输出结果
Process process = Runtime.getRuntime().exec(new String[]{"cmd", "/c", dockerCmd});
int exitCode = process.waitFor();
if (exitCode != 0) {
// 读取错误信息,返回失败结果
String errorMsg = new String(process.getErrorStream().readAllBytes());
log.error("执行 YOLO 检测失败,错误码:{},错误信息:{}", exitCode, errorMsg);
return YoloDetectResult.builder()
.success(false)
.errorMsg("执行检测失败:" + errorMsg)
.build();
}
// 7. 解析命令输出,提取检测到的物体信息
String output = new String(process.getInputStream().readAllBytes());
log.info("YOLO 检测命令输出:{}", output);
List detectObjects = parseDetectResult(output);
// 8. 拼接检测结果图片路径(本地可访问)
String resultImagePath = yoloConfig.getDockerMountPath() + File.separator + "runs" + File.separator + "detect" + File.separator + "detect" + File.separator + fileName;
long costTime = System.currentTimeMillis() - startTime;
// 9. 返回成功结果
return YoloDetectResult.builder()
.success(true)
.detectObjects(detectObjects)
.costTime(costTime)
.resultImagePath(resultImagePath)
.build();
} catch (IOException e) {
log.error("图片检测 IO 异常", e);
return YoloDetectResult.builder()
.success(false)
.errorMsg("IO 异常:" + e.getMessage())
.build();
} catch (InterruptedException e) {
log.error("检测命令执行被中断", e);
Thread.currentThread().interrupt();
return YoloDetectResult.builder()
.success(false)
.errorMsg("检测被中断:" + e.getMessage())
.build();
} catch (Exception e) {
log.error("图片检测异常", e);
return YoloDetectResult.builder()
.success(false)
.errorMsg("检测异常:" + e.getMessage())
.build();
}
}
/**
* 解析 YOLO 检测命令输出,提取目标信息(类别、数量)
* @param output 命令输出字符串
* @return 检测目标列表
*/
private List parseDetectResult(String output) {
List result = new ArrayList<>();
if (output == null || output.isEmpty()) {
return result;
}
// 匹配 "1 cup, 1 tv, 1 mouse" 格式的字符串
Matcher matcher = DETECT_PATTERN.matcher(output);
Map countMap = new HashMap<>();
while (matcher.find()) {
int count = Integer.parseInt(matcher.group(1));
String className = matcher.group(2);
countMap.put(className, countMap.getOrDefault(className, 0) + count);
}
// 转换为 DetectObject 列表(置信度默认 0.9,可根据实际输出优化)
for (Map.Entry entry : countMap.entrySet()) {
result.add(YoloDetectResult.DetectObject.builder()
.className(entry.getKey())
.count(entry.getValue())
.confidence(0.9f)
.build());
}
return result;
}
}
package com.ruoyi.controller;
import com.ruoyi.entity.YoloDetectResult;
import com.ruoyi.util.YoloDetectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
* YOLO 图片检测控制器,提供 HTTP 接口供前端调用
*/
@Slf4j
@RestController
@RequestMapping("/api/yolo")
@RequiredArgsConstructor
public class YoloController {
private final YoloDetectUtil yoloDetectUtil;
/**
* 图片目标检测接口
* @param file 上传的图片文件
* @return 检测结果(JSON 格式)
*/
@PostMapping("/detect")
public ResponseEntity detectImage(@RequestParam("file") MultipartFile file) {
try {
YoloDetectResult result = yoloDetectUtil.detectImage(file);
return ResponseEntity.ok(result);
} catch (Exception e) {
log.error("图片检测接口异常", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(YoloDetectResult.builder()
.success(false)
.errorMsg("接口异常:" + e.getMessage())
.build());
}
}
}
确保Spring Boot启动类扫描到新增的配置类、控制器等组件,修改启动类(如已有则无需新增):
package com.ruoyi;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@Slf4j
@SpringBootApplication(scanBasePackages = "com.ruoyi") // 扫描 com.ruoyi 下所有组件
@EnableConfigurationProperties
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
log.info("Spring Boot 应用启动成功,端口:8889");
}
}
项目启动成功后,用Postman测试图片检测接口,验证集成效果。
http://localhost:8889/api/yolo/detectform-data,key填写 file,value选择“文件”,上传测试图片{
"success": true,
"errorMsg": null,
"detectObjects": [
{
"className": "cup",
"count": 1,
"confidence": 0.9
},
{
"className": "tv",
"count": 1,
"confidence": 0.9
},
{
"className": "mouse",
"count": 1,
"confidence": 0.9
}
],
"costTime": 1200,
"resultImagePath": "D:\yolo_test\runs\detect\detect\xxx_test.jpg"
}
返回结果说明:
success: true:检测成功detectObjects:识别到的物体列表(类别、数量、置信度)costTime:检测耗时(毫秒)resultImagePath:本地带检测框的图片路径,可直接打开查看结果| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 接口调用失败,提示“Docker命令执行失败” | Docker未启动 | 检查Docker Desktop是否正常运行 |
| 接口调用失败,提示“执行检测失败” | 挂载目录权限问题 | 检查Docker Desktop文件共享配置是否正确 |
| 检测速度慢 | 模型较大或CPU性能不足 | 使用更轻量的YOLOv8n模型,或考虑开启GPU加速 |
| 结果图片无法打开 | 结果图片路径不正确 | 检查resultImagePath是否正确,确认Docker检测结果已保存到挂载目录 |
| 图片上传失败,提示文件过大 | 文件大小超出限制 | 调整application.yml中的max-file-size配置 |
这篇文章完整还原了Windows环境下,通过Docker部署YOLOv8并集成到Spring Boot项目的全过程。从环境准备、问题排查到代码集成、接口测试,每一步都给出了详细的实操步骤和解决方案。
基于本文的基础,可以进一步拓展:
让YOLO目标检测能力更好地服务于实际业务场景。如果在实操过程中遇到问题,可参考本文的问题排查部分,或留言交流。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8