springboot调用python文件,python文件使用其他dat文件,适配windows和linux,以及docker环境的方案
介绍
后台是用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.txtClassPathResource 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-cu1212.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-builderWORKDIR /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=1EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/ring.jar"]
相关文章:
springboot调用python文件,python文件使用其他dat文件,适配windows和linux,以及docker环境的方案
介绍 后台是用springboot技术,其他同事做的算法是python,现在的需求是springboot调用python,python又需要调用其他的数据文件,比如dat文件,这个文件是app通过蓝牙获取智能戒指数据以后,保存到后台…...
GSO-YOLO:基于全局稳定性优化的建筑工地目标检测算法解析
论文地址:https://arxiv.org/pdf/2407.00906 1. 论文概述 《GSO-YOLO: Global Stability Optimization YOLO for Construction Site Detection》提出了一种针对建筑工地复杂场景优化的目标检测模型。通过融合全局优化模块(GOM)、稳定捕捉模块(SCM)和创新的AIoU损失函…...
Python 中使用单例模式
有这么一种场景,Web服务中有一个全局资源池,在需要使用的地方就自然而言引用该全局资源池即可,此时可以将该资源池以单例模式实现。随后,需要为某一特殊业务场景专门准备一个全局资源池,于是额外复制一份代码新建了一个…...
系统思考—提升解决动态性复杂问题能力
感谢合作伙伴的信任推荐! 客户今年的人才发展重点之一,是提升管理者应对动态性、复杂性问题的能力。 在深入交流后,系统思考作为关键能力模块,最终被纳入轮训项目——这不仅是一次培训合作,更是一场共同认知的跃迁&am…...
Java基础 - 反射(2)
文章目录 示例5. 通过反射获得类的private、 protected、 默认访问修饰符的属性值。6. 通过反射获得类的private方法。7. 通过反射实现一个工具BeanUtils, 可以将一个对象属性相同的值赋值给另一个对象 接上篇: 示例 5. 通过反射获得类的private、 pro…...
Python proteinflow 库介绍
ProteinFlow是一个开源的Python库,旨在简化蛋白质结构数据在深度学习应用中的预处理过程。以下是其详细介绍: 功能 数据处理:支持处理单链和多链蛋白质结构,包括二级结构特征、扭转角等特征化选项。 数据获取:能够从Protein Data Bank (PDB)和Structural Antibody Databa…...
P1162 洛谷 填涂颜色
题目描述 思考 看数据量 30 而且是个二维的,很像走迷宫 直接深搜! 而且这个就是搜连通块 代码 一开始的15分代码,想的很简单,对dfs进行分类,如果是在边界上,就直接递归,不让其赋值,…...
docker安装nginx,基础命令,目录结构,配置文件结构
Nginx简介 Nginx是一款轻量级的Web服务器(动静分离)/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。其特点是占有内存少,并发能力强. 🔗官网 docker安装Nginx 🐳 一、前提条件 • 已安装 Docker(dock…...
SQLI漏洞公开报告分析
文章目录 1. 闭合 )2. 邀请码|POST参数|时间盲注 | **PostgreSQL**3. POST|order by参数|布尔盲注|Oracle4. SOAP请求|MSSQL|布尔盲注5. MySQL 时间盲注漏洞6. GET|普通回显注入7. ImpressCMS 1.4.2 | CVE | POST | 布尔盲注8. Mysql | post | 布尔/时间盲注9. 登录口 | post |…...
SpringBoot项目部署之启动脚本
一、启动脚本方案 1. 基础启动方式 1.1 直接运行JAR java -jar your-app.jar --spring.profiles.activeprod优点:简单直接,适合快速测试缺点:终端关闭即终止进程 1.2 后台运行 nohup java -jar your-app.jar > app.log 2>&1 &…...
用Django和AJAX创建一个待办事项应用
用Django和AJAX创建一个待办事项应用 推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 用Django和AJAX创建一个待办事项应用让我们创建一个简单的 Django 项目,其中包含不同类型的 A…...
JavaScript:游戏开发的利器
在近年来的科技迅速发展中,JavaScript 已逐渐成为游戏开发领域中最受欢迎的编程语言之一。它的跨平台特性、广泛的社区支持、丰富的库和框架使得开发者能够快速、有效地创建各种类型的游戏。本文将深入探讨 JavaScript 在游戏开发中的优势。 一、跨平台支持 JavaSc…...
C语言今天开始了学习
好多年没有弄了,还是捡起来弄下吧 用的vscode 建议大家参考这个配置 c语言vscode配置 c语言这个语言简单,但是今天听到了一个消息说python 不知道怎么debug。人才真多啊...
Elasticsearch 系列专题 - 第五篇:集群与性能优化
随着数据量和访问量的增长,单节点 Elasticsearch 已无法满足需求。本篇将介绍集群架构、性能优化方法以及常见故障排查,帮助你应对生产环境中的挑战。 1. 集群架构 1.1 节点角色(Master、Data、Ingest 等) Elasticsearch 集群由多个节点组成,每个节点可扮演不同角色: M…...
鸿蒙NEXT开发Preferences工具类(ArkTs)
import { AppUtil } from ./AppUtil; import dataPreferences from ohos.data.preferences; export const DEFAULT_PREFERENCE_NAME: string "SP_HARMONY_UTILS_PREFERENCES"; // Preferences实例的名称。/*** Preferences工具类* author CSDN-鸿蒙布道师* since 20…...
电商素材革命:影刀RPA魔法指令3.0驱动批量去水印,实现秒级素材净化
本文 去除水印实操视频展示电商图片水印处理的困境影刀 RPA 魔法指令 3.0 强势登场利用魔法指令3.0两步实现去除水印操作关于影刀RPA 去除水印实操视频展示 我们这里选择了4张小红书里面比较帅气的图片,但凡用过小红书的都知道,小红书右下角是会有小…...
前端面试题(七):什么是vuex,请解释一下它在Vue中的作用
Vuex 是一个专门为 Vue.js 应用程序开发的状态管理库。它可以集中管理应用的所有状态,并保证状态以一种可预测的方式发生变化。简单来说,Vuex 用来管理 Vue 应用中的数据(即状态),使得数据的传递和共享更加清晰和可靠&…...
笔记:头文件与静态库的使用及组织方式
笔记:头文件与静态库的使用及组织方式 1. 头文件的作用 接口声明:提供函数、类、变量等标识符的声明,供其他模块调用。编译依赖:编译器需要头文件来验证函数调用和类型匹配。避免重复定义:通过包含保护(如…...
ubuntu,react的学习(1)
在此目录下,开启命令行 /home/kt/react 如下操作 tkt4028:~/react$ npm create vitelatest task-manager -- --template react Need to install the following packages: create-vite6.3.1 Ok to proceed? (y) y> npx > cva task-manager --template react…...
【QT】QTreeWidgetItem的checkState/setCheckState函数和isSelected/setSelected函数
目录 1、函数原型1.1 checkState/setCheckState1.2 isSelected/setSelected2、功能用途3、示例QTreeWidget的checkState/setCheckState函数和isSelected/setSelected这两组函数有着不同的用途,下面具体说明: 1、函数原型 1.1 checkState/setCheckState Qt::CheckState QTr…...
CompletableFuture 和 List<CompletableFuture> allOf() join() get() 使用经验
CompletableFuture<Map<Menu, Map<IntentDetail, Double>>> xxx CompletableFuture.supplyAsync(() -> {Map<Menu, Map<IntentDetail, Double>> scores new ConcurrentHashMap<>();// 存储结果scores.computeIfAbsent(menu, k -> n…...
CExercise_09_结构体和枚举_2VS的Debug模式查看它的内存布局,采用结构体数组的方式存储信息,调用函数打印结构体数组.
题目: 下面结构体类型的变量的内存布局是怎样的?请使用VS的Debug模式查看它的内存布局 typedef struct stundent_s {int number;char name[25];char gender;int chinese;int math;int english; } Student;// 结构体对象的声明和初始化 Student s1 { 1, …...
SSRF漏洞技术解析与实战防御指南
一、SSRF漏洞简介 服务端请求伪造(Server-Side Request Forgery, SSRF) 是一种攻击者通过操控服务端发起非预期网络请求的安全漏洞。攻击者利用目标服务器的权限,构造恶意请求访问内网资源、本地系统文件或第三方服务,可能导致…...
CVA6:支持 Linux 的 RISC-V CPU CORE-V
RISC-V 是一种开源的可扩展指令集架构 (ISA),在过去几年中广受欢迎。RISC-V 的主要特性之一是它采用整体架构中性设计,支持浮点运算、加载存储架构、符号扩展加速和多路复用器简化。这使得 RISC-V 成为 FPGA 上软处理器的经济实惠的选择。自 RISC-V ISA …...
水文-用 Coze 工作流打造你的自媒体写作工厂
零代码水文神器:用 Coze 工作流打造你的自媒体写作工厂 作为一个每天被 KPI 追着跑的自媒体运营人,你是不是也常常在想: “这篇文章换个标题就能发第二遍,能不能自动来?” 现在,用 Coze 工作流,…...
轻奢宅家|约克VRF带你畅享舒适居家体验
下班回到家最期待什么?当然是一阵阵沁人心脾的舒适感扑面而来啦! 想要从头到脚都舒服自在?答案就在眼前——就是它!约克VRF中央空调! 约克VRF中央空调独特的臻静降噪技术,让空调运行音轻…...
uniapp微信小程序图片生成水印
整体思路: 用户通过uni.chooseImage选择图片后,获得图片文件的path和size。通过path调用uni.getImageInfo获取图片信息,也就是图片宽高。图片宽高等比缩放至指定大小,不然手机处理起来非常久,因为手机随便拍拍就很大。…...
不用额外下载jar包,idea快速查看使用的组件源码
以nacos为例子,在idea中引入了nacos依赖,就可以查看源码了。 2. idea选择open(不关闭项目直接选择file-open也可以), 在maven的仓库里找到对应的包,打开 2.idea中选择 jar包,选择 add as library 3.这样j…...
网络层-IP地址计算
例1:IP地址二进制与十进制互转 题目: 将二进制IP 11000000.10101000.00000001.00001010 转换为点分十进制。将IP地址 172.16.254.1 转换为二进制格式。 答案与解析: 转换步骤: 每个8位二进制转为十进制: 11000000 →…...
网络通讯协议UDP转发TCP工具_UdpToTcpRelay_双向版
UDP/TCP网络转发器程序说明书 1. 程序概述 本程序是一个高性能网络数据转发工具,支持UDP和TCP协议之间的双向数据转发,并具备以下核心功能: 协议转换:实现UDP↔TCP协议转换数据转换:支持十六进制/ASCII格式的数据转…...
