java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Docker部署YOLOv8集成SpringBoot

Windows环境下Docker部署YOLOv8并集成Spring Boot的完整步骤记录

作者:张道宁

这篇文章主要介绍了Windows环境下Docker部署YOLOv8并集成Spring Boot的完整步骤,本文手把手教你用Docker在Windows上部署YOLOv8,并无缝集成到SpringBoot项目,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

在 AI 目标检测领域,YOLO(You Only Look Once)系列模型以其高效、精准的特点成为主流选择,而 Spring Boot 作为 Java 生态最流行的 Web 开发框架,能轻松将 YOLO 的检测能力封装为可复用的接口。本文将全程还原 Windows 环境下,通过 Docker 部署 YOLOv8 模型,并集成到 Spring Boot 项目的完整流程,包含环境准备、问题排查、代码集成、接口测试,适合有一定 Java 和 Docker 基础的开发者参考。

一、前言:为什么选择 Docker + Spring Boot + YOLOv8?

在本地部署 YOLO 模型时,常常会遇到环境依赖复杂、版本冲突、跨平台兼容性差等问题,而 Docker 能完美解决这些痛点——通过容器化封装 YOLO 运行所需的所有依赖,实现“一次构建,到处运行”。

结合 Spring Boot 的优势,我们可以将 YOLO 的图片检测能力封装为 HTTP 接口,支持前端上传图片、后端返回检测结果,轻松集成到小程序、APP、管理系统等各类业务场景中,实现“检测能力接口化、业务集成轻量化”。

本文核心目标:在 Windows 环境下,完成 Docker 部署 YOLOv8 → 解决部署过程中的常见问题 → 将 YOLO 检测能力集成到 Spring Boot 项目 → 提供可直接调用的图片检测接口。

二、环境准备(必看)

2.1 基础环境清单

2.2 前置环境配置

2.2.1 Docker Desktop 配置(关键)

  1. 安装 Docker Desktop 后,开启 WSL2 后端(性能优于 Hyper-V):

    • 打开 Docker Desktop → Settings → General → 勾选 “Use the WSL 2 based engine”
    • 重启 Docker Desktop,确保服务正常启动(右下角 Docker 图标显示绿色)
  2. 配置文件共享(避免挂载目录权限问题):

    • 打开 Docker Desktop → Settings → Resources → File Sharing
    • 点击 “+” 按钮,添加本地目录 D:\yolo_test(用于挂载 Docker 容器,存储图片和检测结果),保存后重启 Docker

2.2.2 项目基础准备

本文基于已有的 Spring Boot 项目(原有功能包含 Ollama 大模型集成、PGvector 向量存储、阿里云语音识别等),核心是在原有项目基础上新增 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

命令说明:

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

命令说明:

3.4 常见问题排查(重点!)

首次运行大概率会遇到以下问题,本文全程还原排查过程,帮你快速解决。

问题1:FileNotFoundError: /yolo_data/test.jpg does not exist

报错原因:容器内找不到指定图片,核心是两个原因之一:

  1. 本地 D:\yolo_test 目录下没有 test.jpg,或文件名/后缀错误
  2. 目录挂载失败,容器内 /yolo_data 目录为空

解决步骤:

  1. 先在容器内执行 ls /yolo_data,查看挂载目录是否有文件:

    • 如果输出为空:说明挂载失败,重新执行容器运行命令(确保路径是 /d/yolo_test,而非 D:\yolo_test)
    • 如果输出 yolo_data(子目录):说明图片放在了 D:\yolo_test\yolo_data 下,需修改命令为 source=/yolo_data/yolo_data/test.jpg
  2. 确认本地 D:\yolo_test 下有 test.jpg,重新执行检测命令

问题2:检测结果保存到容器内,本地看不到

报错现象:检测成功,但本地 D:\yolo_test 目录下没有检测结果

原因:YOLO 默认将结果保存到容器内 /ultralytics/runs/detect/predict,而非挂载目录

解决方法:

  1. 手动复制结果到挂载目录(容器内执行):

    cp -r /ultralytics/runs/detect/predict /yolo_data/
    
  2. 下次检测直接指定保存路径(推荐),修改命令为:

    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

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 配置
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_test\temp
  # Docker 镜像名称
  docker-image: ultralytics/ultralytics:latest

# 原有配置保留,补充文件上传配置
spring:
  servlet:
    multipart:
      max-file-size: 10MB  # 单文件最大10MB
      max-request-size: 50MB  # 总请求最大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;

/**
 * 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;
}

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;

/**
 * YOLO 检测结果返回实体,用于接口返回 JSON 格式数据
 */
@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 {
        /**
         * 目标类别(如 cup、tv、mouse)
         */
        private String className;

        /**
         * 目标数量
         */
        private int count;

        /**
         * 置信度(0-1),越大越精准
         */
        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;

/**
 * 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<YoloDetectResult.DetectObject> 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<YoloDetectResult.DetectObject> parseDetectResult(String output) {
        List<YoloDetectResult.DetectObject> result = new ArrayList<>();
        if (output == null || output.isEmpty()) {
            return result;
        }

        // 匹配 "1 cup, 1 tv, 1 mouse" 格式的字符串
        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);
        }

        // 转换为 DetectObject 列表(置信度默认 0.9,可根据实际输出优化)
        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;

/**
 * YOLO 图片检测控制器,提供 HTTP 接口供前端调用
 */
@Slf4j
@RestController
@RequestMapping("/api/yolo")
@RequiredArgsConstructor
public class YoloController {
    private final YoloDetectUtil yoloDetectUtil;

    /**
     * 图片目标检测接口
     * @param file 上传的图片文件
     * @return 检测结果(JSON 格式)
     */
    @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") // 扫描 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 测试准备

  1. 启动 Docker Desktop(确保 Docker 服务正常运行)
  2. 启动 Spring Boot 应用(端口 8889)
  3. 准备一张测试图片(jpg/png 格式)

5.2 Postman 测试步骤

  1. 新建 POST 请求,地址:http://localhost:8889/api/yolo/detect
  2. 请求类型选择 form-data,key 填写 file,value 选择“文件”,上传测试图片
  3. 点击发送请求,查看返回结果

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"
}

返回结果说明:

六、常见问题与优化建议

6.1 常见问题

问题可能原因解决方案
接口调用失败,提示“Docker 命令执行失败”Docker 未启动检查 Docker Desktop 是否正常运行
接口调用失败,提示“执行检测失败”挂载目录权限问题检查 Docker Desktop 文件共享配置是否正确
检测速度慢模型较大或 CPU 性能不足使用更轻量的 YOLOv8n 模型,或考虑开启 GPU 加速
结果图片无法打开结果图片路径不正确检查 resultImagePath 是否正确,确认 Docker 检测结果已保存到挂载目录
图片上传失败,提示文件过大文件大小超出限制调整 application.yml 中的 max-file-size 配置

6.2 优化建议

1. 优化检测速度

2. 增强接口安全性

3. 拓展功能

七、总结

本文全程还原了 Windows 环境下,通过 Docker 部署 YOLOv8 模型,并集成到 Spring Boot 项目的完整流程,从环境准备、问题排查到代码集成、接口测试,每一步都提供了详细的实操步骤和解决方案。

核心亮点

  1. 通过 Docker 容器化部署 YOLOv8,避免环境依赖冲突,实现快速部署
  2. 完整解决了 Windows 下 Docker 挂载目录、文件找不到等常见问题
  3. 将 YOLO 检测能力封装为 Spring Boot 接口,可直接集成到各类业务系统
  4. 代码可直接复用,适配原有 Spring Boot 项目(无需从零搭建)

后续拓展方向

基于本文的基础,可以进一步拓展:

让 YOLO 目标检测能力更好地服务于实际业务场景。如果在实操过程中遇到问题,可参考本文的问题排查部分,或留言交流。

到此这篇关于Windows环境下Docker部署YOLOv8并集成Spring Boot的完整步骤的文章就介绍到这了,更多相关Docker部署YOLOv8集成SpringBoot内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文