Python编程

Java调用Python脚本集成(Windows环境)

2022-09-14  本文已影响0人  一路东逝

Java集成调用Python脚本的步骤

环境

Windows 11

JDK11

整合步骤

下载免安装的python包(包含最小Python环境)

https://www.python.org/downloads/release/python-3107/

f52d0e6d713a4e437113a1c2cbae6b798cd5ef2c.png

创建maven项目

0e0c11b45247de6f4374d3bc19b93b108a21d4d4.png

1)pom.xml内容如下:


<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>python-intergration-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>python-intergration-demo</name>
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.11.0</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.36</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.7</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.1</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <createDependencyReducedPom>false</createDependencyReducedPom>
                            <createSourcesJar>false</createSourcesJar>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>org.example.CallPythonScriptDemo</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

2)log4j.properties内容如下:

log4j.rootLogger=debug,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

python-3.10.7-embed-amd64.zip压缩包放到resources目录下

如果需要依赖第三方库,则需要先将此zip解压缩,然后将第三方的包下载下来拷贝到对应的解压缩目录下,然后再进行进行打压缩包的操作。

比如我这里需要引入cv2numpy这两个,我就是把这两个库直接放到目录下面,然后再打压缩。

6744a4917c55f040db6bb3f1366d72fce0267b9d.png

编写python脚本

test_script.py

import logging
import sys

import cv2
import numpy as np

class TestScript:
    def __init__(self):
        self.abc = "123456"

    def process(self, filePath):
        # ...
        res = []

        #输出结果
        print(res)

if __name__ == '__main__':
    inst = TestScript()
    inst.process(sys.argv[1])

需要注意的是:

1)编写python脚本需要注意,python脚本中必须要存在__main__方法,java在调用的时候就是调用的这个main方法。

2)如果需要从调用的地方传参到脚本中,脚本中需要使用sys.argv[1]进行参数接收,下标从1开始。

3)如果需要从脚本中获取结果,不能通过return语句来获取返回值,目前只能通过print进行打印,然后java中通过输入流的方式读取结果。

编写Java代码

CallPythonScriptDemo.java

这段调用python脚本的代码逻辑大概就是将python的运行环境解压缩到一个临时目录中,然后将对应需要执行的python脚本也复制到临时目录中,然后所有的操作就都在临时目录下进行操作。在调用完成之后,再清除临时目录。(这样操作主要是考虑在打成jar包之后,就很难在jar中进行这样的操作了,就需要将相关文件都放在一个临时目录下进行操作。)

package org.example;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Objects;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * Java调用python时,不能通过return语句来获取返回值,而只能通过print将结果写入到标准输出流中,然后在Java中通过标准输入流来读取到返回结果。
 * python第三方的依赖也是需要放在对应的python包目录的下面
 * process.waitFor()返回0调用成功,否则调用失败
 */
@Slf4j
public class CallPythonScriptDemo {
    public static void main(String[] args) throws IOException, InterruptedException {
        //准备环境
        String tempDirPath = System.getProperty("java.io.tmpdir");
        tempDirPath = tempDirPath.replaceAll("\\\\", "/");
        if (!tempDirPath.endsWith("/")) {
            tempDirPath += "/";
        }

        tempDirPath = tempDirPath + "python."+ UUID.randomUUID().toString().replaceAll("-", "") +"/";

        File tempDir = new File(tempDirPath);
        if (!tempDir.exists()) {
            tempDir.mkdirs();
        }

        FileUtils.forceDeleteOnExit(tempDir);

        ZipInputStream zipInputStream = null;
        try {
            InputStream inputStream = CallPythonScriptDemo.class.getResourceAsStream("/python/python-3.10.7-embed-amd64.zip");
            zipInputStream = new ZipInputStream(inputStream);
            ZipEntry zipEntry = null;
            while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                File tempFile = new File(tempDirPath + zipEntry.getName());
                if (zipEntry.isDirectory()) {
                    if (!tempFile.exists()) {
                        tempFile.mkdirs();
                    }
                } else {
                    FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
                    BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
                    int n = 0;
                    byte[] bytes = new byte[1024];
                    while ((n = zipInputStream.read(bytes)) != -1) {
                        bufferedOutputStream.write(bytes, 0, n);
                    }
                    bufferedOutputStream.close();
                    fileOutputStream.close();
                }
                zipInputStream.closeEntry();
            }

            zipInputStream.close();

            inputStream = CallPythonScriptDemo.class.getResourceAsStream("/python/script/test_script.py");
            File tempScriptDir = new File(tempDirPath + "script/");
            if (!tempScriptDir.exists()) {
                tempScriptDir.mkdirs();
            }

            File tempScriptFile = new File(tempDirPath + "script/test_script.py");

            FileUtils.copyInputStreamToFile(inputStream, tempScriptFile);
        } catch (Exception e) {
            throw new RuntimeException("Copy Python Script Fail.", e);
        }

        String pythonExe = tempDirPath + "python-3.10.7-embed-amd64/python.exe";
        String highTempAreaDiscernScript = tempDirPath + "script/test_script.py";

        //准备执行
        String tempImagePath = "D:\\tmp\\data\\eb07654d-be01-46b1-b222-5d0f8340b6bd.jpeg";
        String[] cmdArr = new String[]{pythonExe, highTempAreaDiscernScript, tempImagePath};

        Process process = Runtime.getRuntime().exec(cmdArr);

        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        StringBuilder sb = new StringBuilder();
        String line = null;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }

        reader.close();

        int res = process.waitFor();
        //执行成功就返回0,否则就是其他的值了
        if (Objects.equals(0, res)) {
            log.info("exec result: {}", sb);
        } else {
            log.error("Call python script fail, res = {}", res);
        }

        FileUtils.forceDelete(tempDir);
    }
}

需要注意:

1)process.waitFor()返回0才表示执行成功。

2)上面也讲了,如果需要接收python脚本的结果,不能通过return接收,只能通过输入流的方式读取脚本print的结果。

观察执行结果

2c81b6081eac852271fe707592c6933e3a04d10b.png

以上就是集成的步骤了,后面还需要结合不同的环境(linux环境等)进行动态的切换配置。

您还可查看:
https://www.ithere.net/article/1569714403159638017

上一篇下一篇

猜你喜欢

热点阅读