springboot调用python文件,python文件使用其他dat文件,适配windows和linux,以及docker环境的方案

news2025/4/18 21:01:53

介绍

后台是用springboot技术,其他同事做的算法是python,现在的需求是springboot调用python,python又需要调用其他的数据文件,比如dat文件,这个文件是app通过蓝牙获取智能戒指数据以后,保存到后台,后台需要将数据写入到dat文件里,供python调用,本身难点并不大,主要本地环境死windows,服务器环境是ubuntu,并且是通过docker部署的,这样文件的路径就会产生问题,需要适配

代码部分

先处理一下文件路径问题,路径如下
在这里插入图片描述
如何能保证不同环境都能正确读到文件呢
答案是:使用临时文件复制
在 Spring Boot 调用 Python 脚本的场景中使用临时文件的方式来实现跨平台和 Docker 兼容,主要基于以下几个关键原因:

1. 解决路径一致性问题

问题:不同操作系统路径格式不同(Windows: C:\path,Linux: /path),且 Docker 容器内路径与宿主机不同

方案:临时目录提供统一的绝对路径基准

Path tempDir = Files.createTempDirectory("prefix"); // 自动适应各平台路径格式

2. 解决文件权限问题

问题:直接操作项目资源文件可能因权限不足失败(尤其是 Docker 默认以非 root 用户运行)

方案:临时目录确保可写权限

// 显式设置权限(Linux/Unix需要)
targetPath.toFile().setExecutable(true);

3. 解决资源打包问题

问题:Spring Boot 打包后,resources 下的文件存在于 JAR 内,无法直接通过文件系统访问

方案:运行时复制到临时目录解压

ClassPathResource resource = new ClassPathResource("python/script.py");
Files.copy(resource.getInputStream(), tempPath); // 从JAR内解压到文件系统

4. 解决 Docker 环境隔离

问题:Docker 容器有独立文件系统,无法直接访问宿主机的项目资源

方案:构建镜像时复制资源,运行时使用临时目录

COPY src/main/resources/python /app/python  # 构建时固化路径

5. 多线程安全写入

问题:多线程并发写入同一文件会导致冲突

方案:每个线程使用独立临时文件

Path threadSpecificFile = tempDir.resolve("thread_" + Thread.currentThread().getId() + ".dat");

6. 资源清理保障

问题:运行后残留文件可能积累

方案:标准化清理流程

finally {
    deleteDirectory(tempDir.toFile()); // 确保删除临时文件
}

7. 调试与日志追踪

问题:直接操作原始文件难以追踪运行时状态

方案:临时文件提供独立运行环境

System.out.println("临时目录: " + tempDir); // 明确显示运行时文件位置

代码部分的思路大致是,先将文件复制到临时路径,然后往临时路径的文件里写内容,这个临时路径是可变的,所以不能写死,需要将路径当入参传给python文件

代码

@Service
public class PythonService {

    public String executePythonScript(String waveData) {
        Path tempDir = null;
        try {
            System.out.println("1:"+ DateUtils.dateTimeNow());
            // 1. 创建临时目录(使用NIO API确保跨平台兼容)
            tempDir = Files.createTempDirectory("python_workspace");
            System.out.println("2:"+ DateUtils.dateTimeNow());
            // 2. 复制资源
            copyPythonResourcesToTemp(tempDir);
            System.out.println("3:"+ DateUtils.dateTimeNow());
            //写入数据
            Path tempFile = tempDir.resolve("python/raw_data/bp_106_63.dat");

            try (BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile.toFile()))) {
                writer.write("");//先清空数据
                writer.write(waveData);
            }

            // 3. 构建命令(使用绝对路径)
            String pythonScriptPath = tempDir.resolve("python/realTime_predict.py").toString();
            String[] command = {
                    "python3",
                    pythonScriptPath
            };
            String pythonPath = "C:\\xxxx\\Python\\Python311\\python.exe";  // 替换为你的实际路径
            boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win");
            if (isWindows) {
                command = new String[]{pythonPath, pythonScriptPath,tempFile.toString()};
            } else {
                command = new String[]{"python3",pythonScriptPath,tempFile.toString()};
            }
            System.out.println("4:"+ DateUtils.dateTimeNow());
            // 4. 执行命令
            ProcessBuilder pb = new ProcessBuilder(command);
            pb.directory(tempDir.toFile());
            pb.redirectErrorStream(true);
            System.out.println("5:"+ DateUtils.dateTimeNow());
            Process process = pb.start();
            String output = new BufferedReader(
                    new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))
                    .lines().collect(Collectors.joining("\n"));
            System.out.println("6:"+ DateUtils.dateTimeNow());
            int exitCode = process.waitFor();
            System.out.println("output:"+ output);
            if (exitCode != 0) {
                return "计算出错";
            }
            System.out.println("7:"+ DateUtils.dateTimeNow());
            System.out.println("output:"+ output);
            String[] split = output.split("\n");
            return split[split.length-1];
        } catch (Exception e) {
            e.printStackTrace();
            //throw new RuntimeException("执行Python脚本出错: " + e.getMessage(), e);
        } finally {
            // 生产环境建议保留日志,开发时可清理
            if (tempDir != null) {
                deleteDirectory(tempDir.toFile());
            }
        }
        return "";
    }

    private void copyPythonResourcesToTemp(Path tempDir) throws IOException {
        // 创建python子目录
        Files.createDirectories(tempDir.resolve("python"));
        // 创建必要的子目录结构
        Files.createDirectories(tempDir.resolve("python/raw_data"));
        // 使用Spring的ResourceUtils复制所有文件
        copyResourceToTemp("python/realTime_predict.py", tempDir.resolve("python/realTime_predict.py"));
        copyResourceToTemp("python/best_model.pth", tempDir.resolve("python/best_model.pth"));
        copyResourceToTemp("python/model.py", tempDir.resolve("python/model.py"));
        copyResourceToTemp("python/readData.py", tempDir.resolve("python/readData.py"));
        // 确保先创建raw_data目录再复制数据文件
        copyResourceToTemp("python/raw_data/bp_106_63.dat", tempDir.resolve("python/raw_data/bp_106_63.dat"));

        // 复制requirements.txt(如果存在)
        copyResourceIfExists("python/requirements.txt", tempDir.resolve("python/requirements.txt"));

        // 可选:复制requirements.txt
        ClassPathResource requirements = new ClassPathResource("python/requirements.txt");
        if (requirements.exists()) {
            Files.copy(requirements.getInputStream(),
                    tempDir.resolve("python/requirements.txt"),
                    StandardCopyOption.REPLACE_EXISTING);
        }
    }
    // 新增方法:安全复制资源(仅当资源存在时)
    private void copyResourceIfExists(String resourcePath, Path targetPath) throws IOException {
        ClassPathResource resource = new ClassPathResource(resourcePath);
        if (resource.exists()) {
            try (InputStream in = resource.getInputStream()) {
                Files.copy(in, targetPath, StandardCopyOption.REPLACE_EXISTING);
            }
        }
    }
    // 专用资源复制方法(处理JAR内资源)
    private void copyResourceToTemp(String resourcePath, Path targetPath) throws IOException {
        ClassPathResource resource = new ClassPathResource(resourcePath);
        try (InputStream in = resource.getInputStream()) {
            Files.copy(in, targetPath, StandardCopyOption.REPLACE_EXISTING);
        }
        // 设置可执行权限(Linux/Unix需要)
        targetPath.toFile().setExecutable(true);
    }

    // 辅助方法:递归删除目录
    private void deleteDirectory(File directory) {
        if (directory.exists()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        deleteDirectory(file);
                    } else {
                        file.delete();
                    }
                }
            }
            directory.delete();
        }
    }

}

python部分

if __name__ == "__main__":
    # 测试预测
    if len(sys.argv) < 2:
        print("请提供数据文件路径作为参数")
        sys.exit(1)

    input_file = sys.argv[1]  # 获取Java传递的文件路径参数
    try:
        sbp, dbp = predict_bp(input_file)
        print(f"{sbp}/{dbp} mmHg")
    except Exception as e:
        print(f"错误: {str(e)}", file=sys.stderr)
        sys.exit(1)

input_file就是

command = new String[]{“python3”,pythonScriptPath,tempFile.toString()};

里的tempFile.toString()

注意点

java调用python输出中文是乱码,在python里设置

from pathlib import Path
# 强制设置标准输出编码为UTF-8
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')

python找不到python/raw_data里的文件

 def get_data_file_path(self, filename):
        """获取数据文件的绝对路径"""
        # 1. 尝试在同目录下的raw_data文件夹查找
        script_dir = os.path.dirname(os.path.abspath(__file__))
        data_path = os.path.join(script_dir, "raw_data", filename)

        # 2. 如果在开发环境找不到,尝试在上级目录的raw_data查找
        if not os.path.exists(data_path):
            parent_dir = os.path.dirname(script_dir)
            data_path = os.path.join(parent_dir, "raw_data", filename)

        # 3. 如果还是找不到,尝试在Docker环境路径查找
        if not os.path.exists(data_path):
            data_path = os.path.join("/app/python/raw_data", filename)

        if not os.path.exists(data_path):
            raise FileNotFoundError(f"Data file not found at: {data_path}")

        return data_path

dockerFile注意点

dockerfile这个打包,困扰了我一天,主要遇到了库拉取不下来,下载离线库,又遇到了缺少其他依赖的问题
1.docker build报错

# 第一阶段:构建Python环境(使用官方镜像+国内pip源)
FROM python:3.9-slim AS python-builder

这个就遇到了

ERROR: failed to solve: python:3.9-slim: failed to resolve source metadata for docker.io/library/python:3.9-slim: failed commit on ref “unknown-sha256:b1fd1b5f83b18a7a7377874e3791c8104d5cf26c52677291a31d8805a9a3e5b0”: “unknown-sha256:b1fd1b5f83b18a7a7377874e3791c8104d5cf26c52677291a31d8805a9a3e5b0” failed size validation: 7630 != 7317: failed precondition

还有另一个库openjdk:11-jre-slim,也报错

ERROR: failed to solve: adoptopenjdk:11-jre-hotspot: failed to resolve source metadata for docker.io/library/adoptopenjdk:11-jre-hotspot: failed commit on ref “unknown-sha256:09a07bc840c63d79cfcc70a8960e0cead643b14cfdf6bdbca14a22bd6a9d3991”: “unknown-sha256:09a07bc840c63d79cfcc70a8960e0cead643b14cfdf6bdbca14a22bd6a9d3991” failed size validation: 7634 != 7377: failed precondition

解决的办法是,先docker pull一下这些库,因为比较大,docker build的时候可能会中断
docker pull python:3.9
docker pull openjdk:11-jdk-slim
2.离线包的问题

构建镜像(禁用网络访问)
docker build --no-cache --pull=false --network=none -t ring_1.0.0 .

因为上边的问题,我本来想着都用离线包,把requirements.txt里的依赖库全下载到本地,然后copy到docker里,结果

6.811 ERROR: Could not find a version that satisfies the requirement nvidia-cuda-cupti-cu1212.1.105; platform_system == “Linux” and platform_machine == “x86_64” (from torch) (from versions: none)
6.812 ERROR: No matching distribution found for nvidia-cuda-cupti-cu12
12.1.105; platform_system == “Linux” and platform_machine == “x86_64”

如果一个一个去找,非常麻烦,还不一定匹配,所以还是需要用在线打包
3.在线打包的注意点
一定一定注意,名称是否正确

FROM python:3.9-slim as python-builder

我本来是这个,结果还是一直出错,后来发现是名称错了,可以用

docker images查看一下镜像名称

结果我本地的python镜像名是3.9不是3.9-slim,同样的,FROM openjdk:11-jre-slim 这个也要确认名称

FROM python:3.9 as python-builder

Dockerfile文件内容


# 第一阶段:构建Python环境
FROM python:3.9 as python-builder

WORKDIR /app
COPY ruoyi-admin/src/main/resources/python/requirements.txt .
RUN pip install --user -r requirements.txt && \
    mkdir -p /app/python/raw_data

# 第二阶段:构建Java应用
FROM openjdk:11-jdk-slim

# 安装基础Python环境
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    python3 \
    python3-pip \
    && rm -rf /var/lib/apt/lists/*

# 从python阶段复制依赖
COPY --from=python-builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH

# 设置工作目录
WORKDIR /app

# 复制应用文件
COPY ruoyi-admin/target/ring.jar /ring.jar
COPY ruoyi-admin/src/main/resources/python /app/python

# 设置权限
RUN chmod -R 755 /app/python && \
    find /app/python -name "*.py" -exec chmod +x {} \; && \
    chmod -R 777 /app/python/raw_data  # 确保数据目录可写

# 环境变量
ENV PYTHON_SCRIPT_PATH=/app/python
ENV PYTHONUNBUFFERED=1

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/ring.jar"]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2332387.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GSO-YOLO:基于全局稳定性优化的建筑工地目标检测算法解析

论文地址:https://arxiv.org/pdf/2407.00906 1. 论文概述 《GSO-YOLO: Global Stability Optimization YOLO for Construction Site Detection》提出了一种针对建筑工地复杂场景优化的目标检测模型。通过融合全局优化模块(GOM)​、稳定捕捉模块(SCM)​和创新的AIoU损失函…

系统思考—提升解决动态性复杂问题能力

感谢合作伙伴的信任推荐&#xff01; 客户今年的人才发展重点之一&#xff0c;是提升管理者应对动态性、复杂性问题的能力。 在深入交流后&#xff0c;系统思考作为关键能力模块&#xff0c;最终被纳入轮训项目——这不仅是一次培训合作&#xff0c;更是一场共同认知的跃迁&am…

P1162 洛谷 填涂颜色

题目描述 思考 看数据量 30 而且是个二维的&#xff0c;很像走迷宫 直接深搜&#xff01; 而且这个就是搜连通块 代码 一开始的15分代码&#xff0c;想的很简单&#xff0c;对dfs进行分类&#xff0c;如果是在边界上&#xff0c;就直接递归&#xff0c;不让其赋值&#xff0c…

docker安装nginx,基础命令,目录结构,配置文件结构

Nginx简介 Nginx是一款轻量级的Web服务器(动静分离)/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器。其特点是占有内存少&#xff0c;并发能力强. &#x1f517;官网 docker安装Nginx &#x1f433; 一、前提条件 • 已安装 Docker&#xff08;dock…

用Django和AJAX创建一个待办事项应用

用Django和AJAX创建一个待办事项应用 推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 用Django和AJAX创建一个待办事项应用让我们创建一个简单的 Django 项目,其中包含不同类型的 A…

JavaScript:游戏开发的利器

在近年来的科技迅速发展中&#xff0c;JavaScript 已逐渐成为游戏开发领域中最受欢迎的编程语言之一。它的跨平台特性、广泛的社区支持、丰富的库和框架使得开发者能够快速、有效地创建各种类型的游戏。本文将深入探讨 JavaScript 在游戏开发中的优势。 一、跨平台支持 JavaSc…

C语言今天开始了学习

好多年没有弄了&#xff0c;还是捡起来弄下吧 用的vscode 建议大家参考这个配置 c语言vscode配置 c语言这个语言简单&#xff0c;但是今天听到了一个消息说python 不知道怎么debug。人才真多啊

电商素材革命:影刀RPA魔法指令3.0驱动批量去水印,实现秒级素材净化

本文 去除水印实操视频展示电商图片水印处理的困境​影刀 RPA 魔法指令 3.0 强势登场​利用魔法指令3.0两步实现去除水印操作关于影刀RPA 去除水印实操视频展示 我们这里选择了4张小红书里面比较帅气的图片&#xff0c;但凡用过小红书的都知道&#xff0c;小红书右下角是会有小…

CVA6:支持 Linux 的 RISC-V CPU CORE-V

RISC-V 是一种开源的可扩展指令集架构 (ISA)&#xff0c;在过去几年中广受欢迎。RISC-V 的主要特性之一是它采用整体架构中性设计&#xff0c;支持浮点运算、加载存储架构、符号扩展加速和多路复用器简化。这使得 RISC-V 成为 FPGA 上软处理器的经济实惠的选择。自 RISC-V ISA …

轻奢宅家|约克VRF带你畅享舒适居家体验

下班回到家最期待什么&#xff1f;当然是一阵阵沁人心脾的舒适感扑面而来啦&#xff01;      想要从头到脚都舒服自在&#xff1f;答案就在眼前——就是它&#xff01;约克VRF中央空调&#xff01;      约克VRF中央空调独特的臻静降噪技术&#xff0c;让空调运行音轻…

uniapp微信小程序图片生成水印

整体思路&#xff1a; 用户通过uni.chooseImage选择图片后&#xff0c;获得图片文件的path和size。通过path调用uni.getImageInfo获取图片信息&#xff0c;也就是图片宽高。图片宽高等比缩放至指定大小&#xff0c;不然手机处理起来非常久&#xff0c;因为手机随便拍拍就很大。…

不用额外下载jar包,idea快速查看使用的组件源码

以nacos为例子&#xff0c;在idea中引入了nacos依赖&#xff0c;就可以查看源码了。 2. idea选择open&#xff08;不关闭项目直接选择file-open也可以&#xff09;, 在maven的仓库里找到对应的包&#xff0c;打开 2.idea中选择 jar包&#xff0c;选择 add as library 3.这样j…

网络通讯协议UDP转发TCP工具_UdpToTcpRelay_双向版

UDP/TCP网络转发器程序说明书 1. 程序概述 本程序是一个高性能网络数据转发工具&#xff0c;支持UDP和TCP协议之间的双向数据转发&#xff0c;并具备以下核心功能&#xff1a; 协议转换&#xff1a;实现UDP↔TCP协议转换数据转换&#xff1a;支持十六进制/ASCII格式的数据转…

DIA——边缘检测

1.边缘 边缘是像素的突变位置。 2.常见边缘检测算法 通过找到一阶导数的极值点或者二阶导数的过零点来确定边缘像素的位置。边缘检测通常使用算子&#xff0c;即特定的卷积核。通过差分对离散的像素点求导&#xff0c;然后转化成卷积核进行卷积。使用卷积统一涵盖求导&…

【万象论坛】论坛系统测试报告

一、项目背景 1.1项目起因 在当今数字化浪潮下&#xff0c;互联网技术呈爆发式发展&#xff0c;新技术、新框架、新应用场景不断涌现。从大型企业的数字化转型到初创公司的技术创新&#xff0c;各个层面都离不开互联网技术的支撑。然而&#xff0c;技术人员在学习与工作过程中…

【AI工具】FastGPT:开启高效智能问答新征程

前言 在人工智能飞速发展的当下&#xff0c;各类 AI 工具如雨后春笋般涌现。FastGPT 作为一款基于大语言模型&#xff08;LLM&#xff09;的知识图谱问答系统&#xff0c;凭借其强大的数据处理和模型调校能力&#xff0c;为用户带来了便捷的使用体验。今天&#xff0c;就让我们…

华为数字芯片机考2025合集1已校正

单选 1&#xff0e;以下低功耗措施中&#xff0c;哪种不是降低电路翻转率的方法? A.在不进行算术运算的时候&#xff0c;使这些模块的输入保持不变&#xff0c;不让新的操作数进来 B.采用Gray 码或One‐hot 码作为状态机编码 C.减少电路中的glitch D.重新安排“if‐else”表达…

HackMyVM - todd记录

HackMyVM - toddhttps://mp.weixin.qq.com/s/E_-hepdfY-0veilL1fl2QA

【完整可用】使用openhtmltopdf生成PDF(带SVG)

文章目录 前言OpenHTMLToPDF 简介maven配置依赖字体文件demo代码其他资源放置截图防止maven编译字体文件 前言 AI和网上都是跑不起来或者版本过低的&#xff0c;还有各种BUG的。本文都是查阅官方文档得出的。如果你能跑起来请给个大大的赞&#xff01; OpenHTMLToPDF 简介 Ope…

CTF web入门之爆破

爆破 web21: 打开burp进行抓包 通过对密码进行解析。得知密码是由拼接而来 admin:1 选择要攻击的参数 攻击方式。 选择payload方式 。。添加参数 1&#xff0c;2&#xff0c;3。账号 分隔符 密码 选择加密方式。添加buse64.去掉url字符。不然buse64后&#xff0c;会在u…