商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > Windows环境下Docker部署YOLOv8并集成Spring Boot的完整步骤记录

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

  发布于2026-07-24 阅读(0)

扫一扫,手机访问

前言

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

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

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

在本地部署YOLO模型时,环境依赖复杂、版本冲突、跨平台兼容性差这些头疼的问题几乎人人都会遇到。而Docker恰好能一揽子解决——通过容器化封装YOLO运行所需的所有依赖,实现“一次构建,到处运行”。

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

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

二、环境准备(必看)

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 配置(关键)

  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

命令说明

  • -it:交互式终端,方便在容器内执行命令
  • --rm:容器退出后自动删除,避免残留
  • -v /d/yolo_test:/yolo_data:目录挂载(Windows路径需用 /d/ 替代 D:,Docker才能识别)

3.3 执行 YOLOv8 图片检测(首次实操)

3.3.1 准备测试图片

在本地 D:yolo_test 目录下,放入一张测试图片,命名为 test.jpg注意:文件名和后缀必须完全匹配,避免大小写错误,如 Test.jpgtest.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

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

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

解决步骤

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

    • 如果输出为空:说明挂载失败,重新执行容器运行命令(确保路径是 /d/yolo_test,而非 D:yolo_test
    • 如果输出 yolo_data(子目录):说明图片放在了 D:yolo_testyolo_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_testrunsdetect

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_testrunsdetect 目录,会看到带检测框的 test.jpg,图片上会标注出识别到的物体(如杯子、电视、鼠标等),说明Docker部署YOLOv8成功!

四、Spring Boot 集成 YOLOv8 检测能力(完整代码)

Docker部署YOLOv8成功后,接下来集成到Spring Boot项目,提供HTTP接口,支持前端上传图片、后端返回检测结果(识别的物体、数量、耗时、结果图片路径)。

4.1 项目改造:pom.xml 依赖新增

在原有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、阿里云语音等)全部保留,无需修改。

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_testtemp
  # 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 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 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;
    }
}

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

返回结果说明

  • 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项目的全过程。从环境准备、问题排查到代码集成、接口测试,每一步都给出了详细的实操步骤和解决方案。

核心亮点

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

后续拓展方向

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

  • 视频流实时目标检测
  • 批量图片异步处理
  • 检测结果可视化Dashboard
  • 模型训练与迭代优化

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

本文转载于:https://www.jb51.net/program/367984zjv.htm 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注