Java调用Python脚本集成(Windows环境)
Java集成调用Python脚本的步骤
环境
Windows 11
JDK11
整合步骤
下载免安装的python包(包含最小Python环境)
https://www.python.org/downloads/release/python-3107/
f52d0e6d713a4e437113a1c2cbae6b798cd5ef2c.png创建maven项目
0e0c11b45247de6f4374d3bc19b93b108a21d4d4.png1)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解压缩,然后将第三方的包下载下来拷贝到对应的解压缩目录下,然后再进行进行打压缩包的操作。
比如我这里需要引入cv2
和numpy
这两个,我就是把这两个库直接放到目录下面,然后再打压缩。
编写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环境等)进行动态的切换配置。