美文网首页Python编程
Java调用Python脚本集成(Windows环境)

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

作者: 一路东逝 | 来源:发表于2022-09-14 00:01 被阅读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

    相关文章

      网友评论

        本文标题:Java调用Python脚本集成(Windows环境)

        本文链接:https://www.haomeiwen.com/subject/relkortx.html