跳到主要内容 Windows 下 Docker 部署 YOLOv8 并集成 Spring Boot | 极客日志
Java AI java 算法
Windows 下 Docker 部署 YOLOv8 并集成 Spring Boot 本文介绍在 Windows 环境下使用 Docker 部署 YOLOv8 模型,并将其集成到 Spring Boot 项目的完整流程。内容包括环境配置、Docker 镜像拉取与容器运行、常见问题排查、以及通过 Java 代码调用 Docker 命令实现图片检测接口的开发。最终提供可直接调用的 HTTP 接口,支持上传图片并返回检测结果及耗时。
在 AI 目标检测领域,YOLO(You Only Look Once)系列模型以其高效、精准的特点成为主流选择,而 Spring Boot 作为 Java 生态最流行的 Web 开发框架,能轻松将 YOLO 的检测能力封装为可复用的接口。本文将全程还原 Windows 环境下,通过 Docker 部署 YOLOv8 模型,并集成到 Spring Boot 项目的完整流程,包含环境准备、问题排查、代码集成、接口测试。
一、前言:为什么选择 Docker + Spring Boot + YOLOv8?
在本地部署 YOLO 模型时,常常会遇到环境依赖复杂、版本冲突、跨平台兼容性差等问题,而 Docker 能完美解决这些痛点——通过容器化封装 YOLO 运行所需的所有依赖,实现'一次构建,到处运行'。
结合 Spring Boot 的优势,我们可以将 YOLO 的图片检测能力封装为 HTTP 接口,支持前端上传图片、后端返回检测结果,轻松集成到小程序、APP、管理系统等各类业务场景中,实现'检测能力接口化、业务集成轻量化'。
二、环境准备
2.1 基础环境清单
操作系统 :Windows 10/11(建议开启 WSL2,提升 Docker 性能)
Docker :Docker Desktop(版本 20.0 以上)
JDK :17(Spring Boot 3.2.x 要求)
Maven :3.6+(项目构建)
Spring Boot :3.2.4
YOLO 模型 :YOLOv8n(轻量版,适合 CPU 运行,无需 GPU 加速)
2.2 前置环境配置
2.2.1 Docker Desktop 配置(关键)
安装 Docker Desktop 后,开启 WSL2 后端 (性能优于 Hyper-V):
打开 Docker Desktop → Settings → General → 勾选 'Use the WSL 2 based engine'
重启 Docker Desktop,确保服务正常启动(右下角 Docker 图标显示绿色)
配置文件共享 (避免挂载目录权限问题):
打开 Docker Desktop → Settings → Resources → File Sharing
点击 '+' 按钮,添加本地目录 D:\yolo_test(用于挂载 Docker 容器,存储图片和检测结果),保存后重启 Docker
2.2.2 项目基础准备 本文基于已有的 Spring Boot 项目,核心是在原有项目基础上新增 YOLO 图片检测模块,无需从零搭建 Spring Boot 项目。
三、Docker 部署 YOLOv8 模型(全程实操 + 问题排查) 这一步是核心基础,我们将通过 Docker 拉取 YOLOv8 官方镜像,运行容器并完成图片检测,解决部署过程中最常见的'文件找不到''挂载失败'等问题。
3.1 拉取 YOLOv8 官方 Docker 镜像 打开 PowerShell(管理员模式),执行以下命令拉取官方镜像(内置 YOLOv8 所有依赖,无需手动配置):
docker pull ultralytics/ultralytics:latest
镜像拉取完成后,执行 docker images 可查看镜像是否存在。
3.2 运行 Docker 容器并挂载本地目录 核心命令 (挂载本地 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 才能识别)
3.3 执行 YOLOv8 图片检测(首次实操)
3.3.1 准备测试图片 在本地 D:\yolo_test 目录下,放入一张测试图片,命名为 test.jpg(注意 :文件名和后缀必须完全匹配,避免大小写错误,如 Test.jpg、test.jpeg 都会导致失败)。
3.3.2 执行检测命令 在容器内(命令行显示 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.jpg
save=True:保存检测结果(带检测框的图片)
3.4 常见问题排查 首次运行大概率会遇到以下问题,本文全程还原排查过程,帮你快速解决。
问题 1:FileNotFoundError: /yolo_data/test.jpg does not exist 报错原因 :容器内找不到指定图片,核心是两个原因之一:
本地 D:\yolo_test 目录下没有 test.jpg,或文件名/后缀错误
目录挂载失败,容器内 /yolo_data 目录为空
先在容器内执行 ls /yolo_data,查看挂载目录是否有文件:
如果输出为空:说明挂载失败,重新执行容器运行命令(确保路径是 /d/yolo_test,而非 D:\yolo_test)
如果输出 yolo_data(子目录):说明图片放在了 D:\yolo_test\yolo_data 下,需修改命令为 source=/yolo_data/yolo_data/test.jpg
确认本地 D:\yolo_test 下有 test.jpg,重新执行检测命令
问题 2:检测结果保存到容器内,本地看不到 报错现象 :检测成功,但本地 D:\yolo_test 目录下没有检测结果
原因 :YOLO 默认将结果保存到容器内 /ultralytics/runs/detect/predict,而非挂载目录
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_test\runs\detect
cp -r /ultralytics/runs/detect/predict /yolo_data/
3.5 检测成功验证 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_test\runs\detect 目录,会看到带检测框的 test.jpg,图片上会标注出识别到的物体(如杯子、电视、鼠标等),说明 Docker 部署 YOLOv8 成功!
四、Spring Boot 集成 YOLOv8 检测能力(完整代码) Docker 部署 YOLOv8 成功后,我们将其集成到 Spring Boot 项目,提供 HTTP 接口,支持前端上传图片、后端返回检测结果(识别的物体、数量、耗时、结果图片路径)。
4.1 项目改造:pom.xml 依赖新增 在原有 pom.xml 基础上,新增文件操作、命令执行相关依赖(用于处理图片上传、调用 Docker 命令):
<properties >
<commons-io.version > 2.15.1</commons-io.version >
<commons-lang3.version > 3.14.0</commons-lang3.version >
</properties >
<dependencies >
<dependency >
<groupId > commons-io</groupId >
<artifactId > commons-io</artifactId >
<version > ${commons-io.version}</version >
</dependency >
<dependency >
<groupId > org.apache.commons</groupId >
<artifactId > commons-lang3</artifactId >
<version > ${commons-lang3.version}</version >
</dependency >
<dependency >
<groupId > ch.ethz.ganymed</groupId >
<artifactId > ganymed-ssh2</artifactId >
<version > 262</version >
</dependency >
</dependencies >
注 :原有依赖(Spring Boot Web、Ollama、PGvector、阿里云语音等)全部保留,无需修改。
4.2 项目改造:application.yml 配置新增 在原有配置基础上,新增 YOLO 和 Docker 相关配置,同时补充文件上传限制:
yolo:
docker-mount-path: D:\yolo_test
container-mount-path: /yolo_data
model-name: yolov8n.pt
container-result-path: /yolo_data/runs/detect
local-temp-path: D:\yolo_test\temp
docker-image: ultralytics/ultralytics:latest
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 50MB
4.3 新增核心代码(可直接复制使用) 新增 4 个核心类,实现图片上传、Docker 命令调用、检测结果解析、接口提供,全部放在 com.ruoyi 包下(与原有项目包结构一致)。
4.3.1 配置类:YoloConfig.java(读取 YOLO 配置) package com.ruoyi.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "yolo")
public class YoloConfig {
private String dockerMountPath;
private String containerMountPath;
private String modelName;
private String containerResultPath;
private String localTempPath;
private String dockerImage;
}
4.3.2 实体类:YoloDetectResult.java(封装检测结果) package com.ruoyi.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class YoloDetectResult {
private boolean success;
private String errorMsg;
private List<DetectObject> detectObjects;
private long costTime;
private String resultImagePath;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class DetectObject {
private String className;
private int count;
private float confidence;
}
}
4.3.3 工具类:YoloDetectUtil.java(核心检测逻辑) 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;
@Slf4j
@Component
@RequiredArgsConstructor
public class YoloDetectUtil {
private final YoloConfig yoloConfig;
private static final Pattern DETECT_PATTERN = Pattern.compile("(\\d+)\\s+([a-zA-Z_]+)" );
public YoloDetectResult detectImage (MultipartFile file) {
long startTime = System.currentTimeMillis();
try {
if (file.isEmpty()) {
return YoloDetectResult.builder().success(false ).errorMsg("上传的图片文件为空" ).build();
}
String originalFilename = file.getOriginalFilename();
if (originalFilename == null || !originalFilename.matches(".*\\.(jpg|jpeg|png|bmp)$" )) {
return YoloDetectResult.builder().success(false ).errorMsg("仅支持 jpg/jpeg/png/bmp 格式的图片" ).build();
}
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();
}
}
String fileName = UUID.randomUUID() + "_" + originalFilename;
String localImagePath = yoloConfig.getDockerMountPath() + File.separator + fileName;
File localImageFile = new File (localImagePath);
file.transferTo(localImageFile);
log.info("上传的图片已保存到:{}" , localImagePath);
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("\\" , "/" ),
yoloConfig.getContainerMountPath(),
yoloConfig.getDockerImage(),
yoloConfig.getModelName(),
containerImagePath,
yoloConfig.getContainerResultPath());
log.info("执行 Docker 命令:{}" , dockerCmd);
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();
}
String output = new String (process.getInputStream().readAllBytes());
log.info("YOLO 检测命令输出:{}" , output);
List<YoloDetectResult.DetectObject> detectObjects = parseDetectResult(output);
String resultImagePath = yoloConfig.getDockerMountPath() + File.separator + "runs" + File.separator + "detect" + File.separator + "detect" + File.separator + fileName;
long costTime = System.currentTimeMillis() - startTime;
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();
}
}
private List<YoloDetectResult.DetectObject> parseDetectResult(String output) {
List<YoloDetectResult.DetectObject> result = new ArrayList <>();
if (output == null || output.isEmpty()) {
return result;
}
Matcher matcher = DETECT_PATTERN.matcher(output);
Map<String, Integer> 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);
}
for (Map.Entry<String, Integer> entry : countMap.entrySet()) {
result.add(YoloDetectResult.DetectObject.builder().className(entry.getKey()).count(entry.getValue()).confidence(0.9f ).build());
}
return result;
}
}
4.3.4 控制器:YoloController.java(提供 HTTP 接口) 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;
@Slf4j
@RestController
@RequestMapping("/api/yolo")
@RequiredArgsConstructor
public class YoloController {
private final YoloDetectUtil yoloDetectUtil;
@PostMapping("/detect")
public ResponseEntity<YoloDetectResult> 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());
}
}
}
4.4 启动类验证(确保组件扫描) 确保 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")
@EnableConfigurationProperties
public class Application {
public static void main (String[] args) {
SpringApplication.run(Application.class, args);
log.info("Spring Boot 应用启动成功,端口:8889" );
}
}
五、接口测试(验证集成效果) 项目启动成功后,我们通过 Postman 测试图片检测接口,验证集成效果。
5.1 测试准备
启动 Docker Desktop(确保 Docker 服务正常运行)
启动 Spring Boot 应用(端口 8889)
准备一张测试图片(jpg/png 格式)
5.2 Postman 测试步骤
新建 POST 请求,地址:http://localhost:8889/api/yolo/detect
请求类型选择 form-data,key 填写 file,value 选择'文件',上传测试图片
点击发送请求,查看返回结果
5.3 成功返回示例 {
"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:本地带检测框的图片路径,可直接打开查看结果
六、常见问题与优化建议
6.1 常见问题 问题 可能原因 解决方案 接口调用失败,提示'Docker 命令执行失败' Docker 未启动 检查 Docker Desktop 是否正常运行 接口调用失败,提示'执行检测失败' 挂载目录权限问题 检查 Docker Desktop 文件共享配置是否正确 检测速度慢 模型较大或 CPU 性能不足 使用更轻量的 YOLOv8n 模型,或考虑开启 GPU 加速 结果图片无法打开 结果图片路径不正确 检查 resultImagePath 是否正确,确认 Docker 检测结果已保存到挂载目录 图片上传失败,提示文件过大 文件大小超出限制 调整 application.yml 中的 max-file-size 配置
6.2 优化建议
1. 优化检测速度
开启 Docker GPU 加速 (需安装 NVIDIA 显卡驱动和 Docker GPU 插件)
使用更轻量的 YOLO 模型 (如 YOLOv8n 已是最轻量,可考虑模型量化压缩)
图片预处理 :上传前对图片进行压缩,减小分辨率
2. 增强接口安全性
添加 API Key 权限校验 ,防止匿名调用
添加接口限流 ,避免 Docker 容器被压垮
文件类型严格校验 ,防止恶意文件上传
3. 拓展功能
支持批量图片检测 (上传压缩包,异步处理)
检测结果持久化 :将检测结果存入数据库(如 PostgreSQL),支持历史记录查询
前端可视化 :新增前端页面,实现图片上传、检测结果可视化
视频流检测 :支持 RTSP 视频流实时目标检测
七、总结 本文全程还原了 Windows 环境下,通过 Docker 部署 YOLOv8 模型,并集成到 Spring Boot 项目的完整流程,从环境准备、问题排查到代码集成、接口测试,每一步都提供了详细的实操步骤和解决方案。
核心亮点
通过 Docker 容器化部署 YOLOv8 ,避免环境依赖冲突,实现快速部署
完整解决了 Windows 下 Docker 挂载目录、文件找不到等常见问题
将 YOLO 检测能力封装为 Spring Boot 接口 ,可直接集成到各类业务系统
代码可直接复用 ,适配原有 Spring Boot 项目(无需从零搭建)
后续拓展方向
视频流实时目标检测
批量图片异步处理
检测结果可视化 Dashboard
模型训练与迭代优化
让 YOLO 目标检测能力更好地服务于实际业务场景。如果在实操过程中遇到问题,可参考本文的问题排查部分。
微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online