亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

springboot調(diào)用python文件的詳細(xì)方案

 更新時(shí)間:2025年04月11日 09:38:13   作者:韓zj  
這篇文章主要為大家詳細(xì)介紹了springboot調(diào)用python文件的詳細(xì)方案,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

介紹

后臺(tái)是用springboot技術(shù),其他同事做的算法是python,現(xiàn)在的需求是springboot調(diào)用python,python又需要調(diào)用其他的數(shù)據(jù)文件,比如dat文件,這個(gè)文件是app通過藍(lán)牙獲取智能戒指數(shù)據(jù)以后,保存到后臺(tái),后臺(tái)需要將數(shù)據(jù)寫入到dat文件里,供python調(diào)用,本身難點(diǎn)并不大,主要本地環(huán)境死windows,服務(wù)器環(huán)境是ubuntu,并且是通過docker部署的,這樣文件的路徑就會(huì)產(chǎn)生問題,需要適配

實(shí)現(xiàn)思路

先處理一下文件路徑問題,路徑如下

如何能保證不同環(huán)境都能正確讀到文件呢

答案是:使用臨時(shí)文件復(fù)制

在 Spring Boot 調(diào)用 Python 腳本的場(chǎng)景中使用臨時(shí)文件的方式來(lái)實(shí)現(xiàn)跨平臺(tái)和 Docker 兼容,主要基于以下幾個(gè)關(guān)鍵原因:

1. 解決路徑一致性問題

問題:不同操作系統(tǒng)路徑格式不同(Windows: C:\path,Linux: /path),且 Docker 容器內(nèi)路徑與宿主機(jī)不同

方案:臨時(shí)目錄提供統(tǒng)一的絕對(duì)路徑基準(zhǔn)

Path tempDir = Files.createTempDirectory("prefix"); // 自動(dòng)適應(yīng)各平臺(tái)路徑格式

2. 解決文件權(quán)限問題

問題:直接操作項(xiàng)目資源文件可能因權(quán)限不足失?。ㄓ绕涫?Docker 默認(rèn)以非 root 用戶運(yùn)行)

方案:臨時(shí)目錄確保可寫權(quán)限

// 顯式設(shè)置權(quán)限(Linux/Unix需要)
targetPath.toFile().setExecutable(true);

3. 解決資源打包問題

問題:Spring Boot 打包后,resources 下的文件存在于 JAR 內(nèi),無(wú)法直接通過文件系統(tǒng)訪問

方案:運(yùn)行時(shí)復(fù)制到臨時(shí)目錄解壓

ClassPathResource resource = new ClassPathResource("python/script.py");
Files.copy(resource.getInputStream(), tempPath); // 從JAR內(nèi)解壓到文件系統(tǒng)

4. 解決 Docker 環(huán)境隔離

問題:Docker 容器有獨(dú)立文件系統(tǒng),無(wú)法直接訪問宿主機(jī)的項(xiàng)目資源

方案:構(gòu)建鏡像時(shí)復(fù)制資源,運(yùn)行時(shí)使用臨時(shí)目錄

COPY src/main/resources/python /app/python  # 構(gòu)建時(shí)固化路徑

5. 多線程安全寫入

問題:多線程并發(fā)寫入同一文件會(huì)導(dǎo)致沖突

方案:每個(gè)線程使用獨(dú)立臨時(shí)文件

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

6. 資源清理保障

問題:運(yùn)行后殘留文件可能積累

方案:標(biāo)準(zhǔn)化清理流程

finally {
    deleteDirectory(tempDir.toFile()); // 確保刪除臨時(shí)文件
}

7. 調(diào)試與日志追蹤

問題:直接操作原始文件難以追蹤運(yùn)行時(shí)狀態(tài)

方案:臨時(shí)文件提供獨(dú)立運(yùn)行環(huán)境

System.out.println("臨時(shí)目錄: " + tempDir); // 明確顯示運(yùn)行時(shí)文件位置

代碼部分的思路大致是,先將文件復(fù)制到臨時(shí)路徑,然后往臨時(shí)路徑的文件里寫內(nèi)容,這個(gè)臨時(shí)路徑是可變的,所以不能寫死,需要將路徑當(dāng)入?yún)鹘opython文件

完整代碼

@Service
public class PythonService {

    public String executePythonScript(String waveData) {
        Path tempDir = null;
        try {
            System.out.println("1:"+ DateUtils.dateTimeNow());
            // 1. 創(chuàng)建臨時(shí)目錄(使用NIO API確保跨平臺(tái)兼容)
            tempDir = Files.createTempDirectory("python_workspace");
            System.out.println("2:"+ DateUtils.dateTimeNow());
            // 2. 復(fù)制資源
            copyPythonResourcesToTemp(tempDir);
            System.out.println("3:"+ DateUtils.dateTimeNow());
            //寫入數(shù)據(jù)
            Path tempFile = tempDir.resolve("python/raw_data/bp_106_63.dat");

            try (BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile.toFile()))) {
                writer.write("");//先清空數(shù)據(jù)
                writer.write(waveData);
            }

            // 3. 構(gòu)建命令(使用絕對(duì)路徑)
            String pythonScriptPath = tempDir.resolve("python/realTime_predict.py").toString();
            String[] command = {
                    "python3",
                    pythonScriptPath
            };
            String pythonPath = "C:\\xxxx\\Python\\Python311\\python.exe";  // 替換為你的實(shí)際路徑
            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. 執(zhí)行命令
            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 "計(jì)算出錯(cuò)";
            }
            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("執(zhí)行Python腳本出錯(cuò): " + e.getMessage(), e);
        } finally {
            // 生產(chǎn)環(huán)境建議保留日志,開發(fā)時(shí)可清理
            if (tempDir != null) {
                deleteDirectory(tempDir.toFile());
            }
        }
        return "";
    }

    private void copyPythonResourcesToTemp(Path tempDir) throws IOException {
        // 創(chuàng)建python子目錄
        Files.createDirectories(tempDir.resolve("python"));
        // 創(chuàng)建必要的子目錄結(jié)構(gòu)
        Files.createDirectories(tempDir.resolve("python/raw_data"));
        // 使用Spring的ResourceUtils復(fù)制所有文件
        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"));
        // 確保先創(chuàng)建raw_data目錄再?gòu)?fù)制數(shù)據(jù)文件
        copyResourceToTemp("python/raw_data/bp_106_63.dat", tempDir.resolve("python/raw_data/bp_106_63.dat"));

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

        // 可選:復(fù)制requirements.txt
        ClassPathResource requirements = new ClassPathResource("python/requirements.txt");
        if (requirements.exists()) {
            Files.copy(requirements.getInputStream(),
                    tempDir.resolve("python/requirements.txt"),
                    StandardCopyOption.REPLACE_EXISTING);
        }
    }
    // 新增方法:安全復(fù)制資源(僅當(dāng)資源存在時(shí))
    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);
            }
        }
    }
    // 專用資源復(fù)制方法(處理JAR內(nèi)資源)
    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);
        }
        // 設(shè)置可執(zhí)行權(quán)限(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__":
    # 測(cè)試預(yù)測(cè)
    if len(sys.argv) < 2:
        print("請(qǐng)?zhí)峁?shù)據(jù)文件路徑作為參數(shù)")
        sys.exit(1)

    input_file = sys.argv[1]  # 獲取Java傳遞的文件路徑參數(shù)
    try:
        sbp, dbp = predict_bp(input_file)
        print(f"{sbp}/{dbp} mmHg")
    except Exception as e:
        print(f"錯(cuò)誤: {str(e)}", file=sys.stderr)
        sys.exit(1)

input_file就是

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

里的tempFile.toString()

注意點(diǎn)

java調(diào)用python輸出中文是亂碼,在python里設(shè)置

from pathlib import Path
# 強(qiáng)制設(shè)置標(biāo)準(zhǔn)輸出編碼為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):
        """獲取數(shù)據(jù)文件的絕對(duì)路徑"""
        # 1. 嘗試在同目錄下的raw_data文件夾查找
        script_dir = os.path.dirname(os.path.abspath(__file__))
        data_path = os.path.join(script_dir, "raw_data", filename)

        # 2. 如果在開發(fā)環(huán)境找不到,嘗試在上級(jí)目錄的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環(huán)境路徑查找
        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注意點(diǎn)

dockerfile這個(gè)打包,困擾了我一天,主要遇到了庫(kù)拉取不下來(lái),下載離線庫(kù),又遇到了缺少其他依賴的問題

1.docker build報(bào)錯(cuò)

# 第一階段:構(gòu)建Python環(huán)境(使用官方鏡像+國(guó)內(nèi)pip源)
FROM python:3.9-slim AS python-builder

這個(gè)就遇到了

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

還有另一個(gè)庫(kù)openjdk:11-jre-slim,也報(bào)錯(cuò)

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一下這些庫(kù),因?yàn)楸容^大,docker build的時(shí)候可能會(huì)中斷

docker pull python:3.9
docker pull openjdk:11-jdk-slim

2.離線包的問題

構(gòu)建鏡像(禁用網(wǎng)絡(luò)訪問)

docker build --no-cache --pull=false --network=none -t ring_1.0.0 .

因?yàn)樯线叺膯栴},我本來(lái)想著都用離線包,把requirements.txt里的依賴庫(kù)全下載到本地,然后copy到docker里,結(jié)果

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”

如果一個(gè)一個(gè)去找,非常麻煩,還不一定匹配,所以還是需要用在線打包

3.在線打包的注意點(diǎn)

一定一定注意,名稱是否正確

FROM python:3.9-slim as python-builder

我本來(lái)是這個(gè),結(jié)果還是一直出錯(cuò),后來(lái)發(fā)現(xiàn)是名稱錯(cuò)了,可以用

docker images查看一下鏡像名稱

結(jié)果我本地的python鏡像名是3.9不是3.9-slim,同樣的,F(xiàn)ROM openjdk:11-jre-slim 這個(gè)也要確認(rèn)名稱

FROM python:3.9 as python-builder

Dockerfile文件內(nèi)容

# 第一階段:構(gòu)建Python環(huán)境
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

# 第二階段:構(gòu)建Java應(yīng)用
FROM openjdk:11-jdk-slim

# 安裝基礎(chǔ)Python環(huán)境
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    python3 \
    python3-pip \
    && rm -rf /var/lib/apt/lists/*

# 從python階段復(fù)制依賴
COPY --from=python-builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH

# 設(shè)置工作目錄
WORKDIR /app

# 復(fù)制應(yīng)用文件
COPY ruoyi-admin/target/ring.jar /ring.jar
COPY ruoyi-admin/src/main/resources/python /app/python

# 設(shè)置權(quán)限
RUN chmod -R 755 /app/python && \
    find /app/python -name "*.py" -exec chmod +x {} \; && \
    chmod -R 777 /app/python/raw_data  # 確保數(shù)據(jù)目錄可寫

# 環(huán)境變量
ENV PYTHON_SCRIPT_PATH=/app/python
ENV PYTHONUNBUFFERED=1

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

以上就是springboot調(diào)用python文件的詳細(xì)方案的詳細(xì)內(nèi)容,更多關(guān)于springboot調(diào)用python的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring IoC學(xué)習(xí)之ApplicationContext中refresh過程詳解

    Spring IoC學(xué)習(xí)之ApplicationContext中refresh過程詳解

    這篇文章主要給大家介紹了關(guān)于Spring IoC學(xué)習(xí)之ApplicationContext中refresh過程的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • java實(shí)現(xiàn)自定義時(shí)鐘并實(shí)現(xiàn)走時(shí)功能

    java實(shí)現(xiàn)自定義時(shí)鐘并實(shí)現(xiàn)走時(shí)功能

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)自定義時(shí)鐘并實(shí)現(xiàn)走時(shí)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • Spring boot文件路徑映射配置代碼實(shí)例

    Spring boot文件路徑映射配置代碼實(shí)例

    這篇文章主要介紹了Spring boot文件路徑映射配置代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • gRPC在Java中的實(shí)現(xiàn)與應(yīng)用詳解

    gRPC在Java中的實(shí)現(xiàn)與應(yīng)用詳解

    gRPC是由Google開發(fā)的高性能、開源的通用遠(yuǎn)程過程調(diào)用(RPC)框架,本文將詳細(xì)介紹如何在Java中使用gRPC,包括服務(wù)定義、服務(wù)器端實(shí)現(xiàn)、客戶端調(diào)用以及一些高級(jí)特性,我們將通過代碼示例來(lái)幫助理解gRPC的工作原理,需要的朋友可以參考下
    2024-06-06
  • java+jsp+struts2實(shí)現(xiàn)發(fā)送郵件功能

    java+jsp+struts2實(shí)現(xiàn)發(fā)送郵件功能

    這篇文章主要為大家詳細(xì)介紹了java+jsp+struts2實(shí)現(xiàn)發(fā)送郵件功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • PostMan傳@RequestParam修飾的數(shù)組方式

    PostMan傳@RequestParam修飾的數(shù)組方式

    這篇文章主要介紹了PostMan傳@RequestParam修飾的數(shù)組方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java獲取IP地址以及MAC地址的示例代碼

    Java獲取IP地址以及MAC地址的示例代碼

    IP地址是用于在網(wǎng)絡(luò)上識(shí)別設(shè)備的唯一地址,而MAC地址是設(shè)備的物理地址,本文主要介紹了Java獲取IP地址以及MAC地址的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-04-04
  • 如何根據(jù)帶賬號(hào)密碼的WSDL地址生成JAVA代碼

    如何根據(jù)帶賬號(hào)密碼的WSDL地址生成JAVA代碼

    這篇文章主要介紹了如何根據(jù)帶賬號(hào)密碼的WSDL地址生成JAVA代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Java?ASM使用logback日志級(jí)別動(dòng)態(tài)切換方案展示

    Java?ASM使用logback日志級(jí)別動(dòng)態(tài)切換方案展示

    這篇文章主要介紹了Java?ASM使用logback日志級(jí)別動(dòng)態(tài)切換方案展示,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • IDEA運(yùn)行spring項(xiàng)目時(shí),控制臺(tái)未出現(xiàn)的解決方案

    IDEA運(yùn)行spring項(xiàng)目時(shí),控制臺(tái)未出現(xiàn)的解決方案

    文章總結(jié)了在使用IDEA運(yùn)行代碼時(shí),控制臺(tái)未出現(xiàn)的問題和解決方案,問題可能是由于點(diǎn)擊圖標(biāo)或重啟IDEA后控制臺(tái)仍未顯示,解決方案提供了解決方法,包括通過右三角Run運(yùn)行(快捷鍵:Alt+42)或以Debug運(yùn)行(快捷鍵:Alt+5)來(lái)解決
    2025-01-01

最新評(píng)論