springboot调用python脚本的基础写法及函数封装

本文详细介绍了如何在SpringBoot应用中调用Python脚本,包括使用sys.argv处理命令行参数,异步调用避免阻塞,以及处理可能出现的异常和路径问题。还提供了完整的代码示例和常见问题解决方案。
摘要由CSDN通过智能技术生成

SpringBoot调用python脚本

一、简单调用

Python脚本:

import sys

param1 = sys.argv[1]
param2 = sys.argv[2]
#在Python脚本中,你可以使用sys.argv来接收命令行参数。sys.argv[0]是脚本的名称,sys.argv[1]是第一个参数,以此类推

# 在脚本中处理参数
print("Parameter 1:", param1)
print("Parameter 2:", param2)

Java代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class PythonScriptExecutor {
    public static void main(String[] args) {
        try {
            // 这里最好为绝对路径
            String pythonScriptPath = "path/to/your/script.py";  // 替换为你的Python脚本路径
            String param1 = "Hello"; //传递到脚本的参数
            String param2 = "World";
            // "python" 是你电脑的python可执行程序的路径,告诉函数需要用什么来运行脚本
            // 也可替换为python.exe的绝对路径
            // 这条语句等于在黑窗口中输入:python path/to/your/script.py Hello World
            ProcessBuilder processBuilder = new ProcessBuilder("python", pythonScriptPath, param1, param2);
            
            // 启动python.exe开始执行脚本
            Process process = processBuilder.start();

            // InputStreamReader:这个类用于将输入流(比如进程的输入流)转换为字符流,字符流可以被BufferedReader读取。
            // 它接受一个InputStream作为参数,并提供一个read方法,该方法返回一个character.process。
            // gettinputstream():这个方法返回进程的输入流。
            // 流程具有可用于与流程通信的输入和输出流。
            // 在这种情况下,输入流用于从进程的标准输入中读取字符。
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

            String line;
           
            // 创建了BufferedReader,可以使用BufferedReader的readLine()或read()方法从进程的输入流中读取字符。
            while ((line = reader.readLine()) != null) {
                // 读取Python脚本的输出,并处理返回的数据
                System.out.println("Python script output: " + line);
                // ... 在此处进行进一步的处理
            }

            int exitCode = process.waitFor();// 等待脚本执行结束后获取返回的状态码
            System.out.println("Python script executed with exit code " + exitCode);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

二、扩展写法:

异步构建

public class PythonScriptExecutor {
    public String hello(){

        String account = "admin";
        // 设置账号
        String pwd = "1234";
        // 设置密码
        String path = classScheduleConstant.Python_Path;
        // 设置Python脚本的路径

        //    两个线程解决调用python一直加载,执行一半执行不下去的情况
        //    这句相当于调用小黑窗执行命令,填详细地址(python) ,文件名
        ProcessBuilder pb = new ProcessBuilder(Python_File_Path, path, account, pwd);
        pb.redirectErrorStream(true); //将error数据流重定向
        Process p = null; //开启进程
        try {
            p = pb.start();
            new Thread(new ProcessTestRunnable(p)).start(); //开启新的线程接受数据流的输入
            p.waitFor();

        } catch (Exception e) {
                e.printStackTrace();
        }
        // 返回结果
        return Res;

    }
    // 创建一个ProcessTestRunnable类,用于接收进程的输出
    class ProcessTestRunnable implements Runnable{
        Process p;
        BufferedReader br;
        ProcessTestRunnable(Process p){
            this.p = p;
        }

        @Override
        public void run() {
            try {
                InputStreamReader isr = new InputStreamReader(p.getInputStream(), "GBK");
                br = new BufferedReader(isr);
                String line = null;
                // 读取每一行数据
                while ((line = br.readLine()) != null) {
                    System.out.println(line);
                    // 将数据打印到控制台
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

}

三、封装函数及使用(可适用其他语言)

public class ProcessUtil {

    private static ProcessBuilder processBuilder = new ProcessBuilder();

    public static Process exec(List<String> commend) {
        Process process = null;
        try {
            String[] commends = new String[commend.size()];
            //将一个可遍历的commends对象流转换成一个新的String数组。实现原理是将commends对象流中的元素依次收集并创建一个新的String数组。
            //这种转换方法的优点是它避免了直接将commends对象转换为String[]数组时可能出现的并发问题。如果commends是一个 parallelCollection,
            // 那么直接转换可能会导致并发问题,因为commends的元素可能会被并发访问。使用toArray方法可以确保线程安全,同时提高性能。
            commend.toArray(commends);
            processBuilder.command(commends);
            process = processBuilder.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return process;
    }

    public static String getOutput(Process process) {
        String output = null;
        BufferedReader reader = null;
        try {
            if (process != null) {
                StringBuffer stringBuffer = new StringBuffer();
                // StandardCharsets.UTF_8 是 Java 中的一个常量,表示 UTF-8 字符编码。它是 Java 标准库中的一个类,用于定义常用的字符编码。
                reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));
                while (reader.read() != -1){
                    stringBuffer.append(reader.readLine());
                }
                output = stringBuffer.toString();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        closeQuietly(reader);
        return output;
    }

    public static String getError(Process process) {
        String errput = null;
        BufferedReader reader = null;
        try {
            if (process != null) {
                StringBuffer stringBuffer = new StringBuffer();
                reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                while (reader.read() != -1){
                    stringBuffer.append("\n" + reader.readLine());
                }
                errput = stringBuffer.toString();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        closeQuietly(reader);
        return errput;
    }

    public static void closeQuietly(Reader reader) {
        try {
            if (reader != null) {
                reader.close();
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

    public static void destroy(Process process) {
        if (process != null) {
            process.destroyForcibly();
        }
    }

使用

	String param1 = "Hello"; //传递到脚本的参数
	String param2 = "World";
	List<String> commend = new ArrayList<>();
	commend.add( classScheduleConstant.Python_Path );
	commend.add( classScheduleConstant.Python_Script_Path );
	commend.add( param1 );
	commend.add( param2 );
	
	//执行脚本命令
	Process process = ProcessUtil.exec(commend);
	//获取执行结果
	String message = ProcessUtil.getOutput(process);

踩坑:

1)坑之一

python脚本路径必须为绝对路径

在这里插入图片描述

2)坑之二

脚本文件如果存放在springboot项目文件内,则脚本内所使用的调用外部文件路径必须为“内容根的路径”或文件的”绝对路径“,即:
在这里插入图片描述

3)坑之三

当message没有任何返回结果时,多半是脚本内部编写有问题,此时调用getError方法即可在控制台打印错误信息或者参考异步扩展写法中的重定向错误

参考链接:

链接一:java springboot 调用python脚本,含第三方库_springboot 引用第三方包 org.python-CSDN博客

链接二:BufferReader中的ready()和readLine()方法的使用和注意事项_bufferreader.readline-CSDN博客

链接三:完整封装java执行脚本工具——ProcessBuilder_java 数据库执行脚本工具-CSDN博客
ttps://blog.csdn.net/uutale/article/details/120839300)

链接二:BufferReader中的ready()和readLine()方法的使用和注意事项_bufferreader.readline-CSDN博客

链接三:完整封装java执行脚本工具——ProcessBuilder_java 数据库执行脚本工具-CSDN博客

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值