python执行是由多种方式的,可以在java程序中编写python代码执行,也可以提前写脚本,通过执行脚本文件方式执行。由于个人需求,脚本内容动态变化,不适合写在代码中,故,介绍执行脚本的方式:
话不多说,上代码
通过java执行脚本文件,
关键代码(执行脚本的):
Runtime.getRuntime().exec(String[] args)
注意事项:
args[0]是python执行的命令,windows和linux注意区分,args[1]是文件地址,是本地的地址噢,不是http网络文件。
下面是执行代码:
public class ScriptUtils {
private static Logger logger = LoggerFactory.getLogger(ScriptUtils.class);
/**
* 执行python脚本
* @param path 文件地址:比如 D:\\xxx\\helloWorld.py
*/
public static Boolean exePython(String path){
logger.info("======python start");
boolean success = false;
Process proc = null;
try {
// linux 用 "python3", windows 用python.exe的绝对路径("D:\\xxx\\Python\\Python39-32\\python.exe")
String[] args1 = new String[]{"python3", path};
proc = Runtime.getRuntime().exec(args1);
// 读写日志线程,分成两个也是为了避免线程堵塞之类的问题,具体原因网络上有很多说明
Thread thread1 = new Thread(new StreamReaderThread(proc.getInputStream(),"info.txt"));
Thread thread2 = new Thread(new StreamReaderThread(proc.getErrorStream(),"error.txt"));
thread2.start();
//必须后执行,否则正确消息容易接收不到
thread1.start();
// result是结果,具体有哪些值,可以自己去查一下
int result = proc.waitFor();
success = result != -1;
//等待后台线程读写完毕
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
throw new ServerException("python error:" + e);
} finally {
try {
proc.getErrorStream().close();
proc.getInputStream().close();
} catch (IOException e) {
e.printStackTrace();
}
proc.destroy();
logger.info("======python end");
}
return success;
}
/**
* 执行groovy脚本的(附赠哈哈,都是脚本执行嘛)
*/
public static Boolean exeGroovy(String path) {
logger.info("======groovy start");
boolean success = false;
try {
GroovyShell groovyShell = new GroovyShell();
Object result = groovyShell.evaluate(new File(path));
success = true;
} catch (Exception e) {
e.printStackTrace();
throw new ServerException("groovy error:" + e);
} finally {
logger.info("======groovy end");
}
return success;
}
}
日志输出:
public class StreamReaderThread implements Runnable {
private Logger logger = LoggerFactory.getLogger(StreamReaderThread.class);
/*
* 输出流
*/
private InputStream inputStream;
/*
* 输出信息保存的文件名称
*/
private String logName;
public StreamReaderThread(InputStream inputStream, String logName) {
this.inputStream = inputStream;
this.logName = logName;
}
/**
* FileWriter将日志写入某文件
* 也可以用logger打印日志记录。
*/
public void run() {
BufferedReader in = null;
FileWriter fwriter = null;
try {
in = new BufferedReader(new InputStreamReader(this.inputStream, "gbk"));
fwriter = new FileWriter(logName, true);
String line = null;
while ((line = in.readLine()) != null) {
fwriter.write(line);
logger.info(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
fwriter.flush();
fwriter.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
如果是网络文件的话,先下载到本机:
/**
* 下载网络文件到本机
* @Param urlString 网络文件下载地址:http://xxx.xx.xx
* @Param fileName 文件名称
*/
public static String downloadFile(String urlString, String fileName) throws IOException {
makeDir(scriptConfig.getUploadPath());
String filePath = scriptConfig.getUploadPath() + SnowflakeIdUtils.nextId() + fileName;
// 构造URL
URL url = new URL(urlString);
// 打开连接
URLConnection con = url.openConnection();
// 设置Java服务器代理连接,要不然报错403
// 浏览器可以访问此url图片并显示,但用Java程序就不行报错Server returned HTTP response code:403 for URL
// 具体原因:服务器的安全设置不接受Java程序作为客户端访问(被屏蔽),解决办法是设置客户端的User Agent
con.setRequestProperty("User-Agent", "Mozilla/4.0(compatible;MSIE 5.0;Windows NT;DigExt)");
// 输入流
InputStream is = con.getInputStream();
// 1K的数据缓冲
byte[] bs = new byte[1024];
// 读取到的数据长度
int len;
// 输出的文件流
OutputStream os = new FileOutputStream(filePath);
// 开始读取
while ((len = is.read(bs)) != -1) {
os.write(bs, 0, len);
}
// 完毕,关闭所有链接
os.close();
is.close();
return filePath;
}
private static void makeDir(String fileFolder) {
File file = new File(fileFolder);
if (!file.exists() && !file.isDirectory()) {
file.mkdir();
}
}
关于项目发布
这里再啰嗦几句吧,在编写python脚本时,我用的是windows环境,项目上线后,是放到linux环境的,通过k8s管理的。当时遇到了一个问题就是k8s环境的镜像是java的,没有集成python,这里注意要自定义一个jdk+python都支持的镜像。(如果python有用到第三方库 也要打包进来噢)
String[] args1 = new String[]{"python3", path};
proc = Runtime.getRuntime().exec(args1);
args1[0]的python3我写到了配置文件里,通过配置读取,这样在windows/linux切换时,只需要加载不同的配置文件就好了。
关于其他方式执行:
maven引入jython其实也是可以执行脚本文件 或者 脚本内容的,并且使用方式非常的简单。没有用这个是因为jython的库是有限的。当使用第三方库或自定义库的时候就有些局限了(我也不知道有没有解决方案,懒得找),所以我还是更倾向于使用Runtime.getRuntime().exec();
jython方法也提供一下吧:
1.pom文件中引用
org.python
jython-standalone
2.7.0
2. 脚本文件执行
PythonInterpreter interpreter = new PythonInterpreter();
interpreter.execfile("D:\\xxx\\helloWorld.py");
3.脚本内容执行
PythonInterpreter interpreter = new PythonInterpreter();
interpreter.exec("a='hello world';");
interpreter.exec("print a;");
4.还有执行指定方法,给python传参的之类的,看参考文献吧,不举例了。
参考文献
(15条消息) Java执行python脚本_为山九仞的博客-CSDN博客_java执行python脚本https://blog.csdn.net/qq_41665121/article/details/105675360?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7Edefault-1-105675360-blog-126644076.pc_relevant_multi_platform_whitelistv4eslandingrelevant&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7Edefault-1-105675360-blog-126644076.pc_relevant_multi_platform_whitelistv4eslandingrelevant&utm_relevant_index=2