Java调用Python项目:传参、不传参和服务器部署详解

一、无参数调用举例

  1. 创建一个python文件,写入如下代码:

import numpy as np

array = np.arange(16).reshape(4, 4)
print(array)

a = 10000
b = 20000
c = a + b

print("result from Python: ")
print(c)

  1. 创建一个Java文件

编写入如下代码(代码的意思已经添加注释)

/**
 * @author 段
 * @date 2023/3/22 17:06
 */
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class CallPython_Demo1 {
  public static void main(String[] args) {
         /*Java 调用Python 方式一: 使用Runtime.getRuntime()执行python脚本  例子1:无参数的例子 */
        Process proc;
        try {
            // 执行py文件,注意:这里的命令都最好用绝对路径,到底用哪一个环境的下的Python,比如env下面的tf2的环境;具体执行哪个python文件,也是绝对路径
            proc = Runtime.getRuntime().exec("D:\\Anaconda3\\envs\\tf2\\python.exe " +
                    "D:\\pythonProject\\SA_Experiment3_JavaCallPython\\toBeCalled1.py");

             // 用输入输出流来截取结果
            BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));

             //输出结果到Java的控制台
             String line = null;
             while ((line = in.readLine()) != null) {
                 System.out.println(line);
                 }

             in.close();
             proc.waitFor();
             } catch (IOException e) {
             e.printStackTrace();
             } catch (InterruptedException e) {
             e.printStackTrace();
             }
         }
 }

3、结果如下说明运行成功

二、有参数时调用举例

1、创建Java文件并写入代码

/**
 * @author 段
 * @date 2023/3/22 17:35
 */
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class CallPython_Demo2 {
    public static void main(String[] args) {

      int a=123;
      int b=456;

      try {
          String[] arguments = new String[] {
                  "D:\\Anaconda3\\envs\\tf2\\python.exe",
                  "D:\\pythonProject\\SA_Experiment3_JavaCallPython\\toBeCalled2.py",
                  String.valueOf(a),
                  String.valueOf(b) };

            // 执行py文件,注意:这里的命令都最好用绝对路径,到底用哪一个环境的下的Python,比如env下面的tf2的环境;具体执行哪个python文件,也是绝对路径
          Process proc = Runtime.getRuntime().exec(arguments); // 执行py文件,
             // 用输入输出流来截取结果
          BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));

             //输出结果到Java的控制台
          String line = null;
          while ((line = in.readLine()) != null) {
                 System.out.println(line);
                 }

          in.close();
          proc.waitFor();
      } catch (IOException e) {
          e.printStackTrace();
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
  }
 }

2、创建python文件并写入代码

import sys


def add(a, b):
    return (a + b)


if __name__ == '__main__':
    print(sys.argv)
    # 1、sys.argv 是获取运行python文件的时候命令行参数,且以list形式存储参数
    # 2、sys.argv[0]表示代码本身文件路径
    # 所以,java调用时,执行该程序,该python程序本身为sys.argv[0], sys.argv[1]、sys.argv[2]

    prameters = []
    for i in range(1, len(sys.argv)):
        print(i)
        prameters.append(int(sys.argv[i]))

    result = add(prameters[0], prameters[1])
    print(result)

运行结果如下说明调用成功!

三、将Python脚本做成服务,供Java调用

1、安装Python程序依赖的package,pillow和image

2、创建Java文件写入如下代码

/**
 * @author 段
 * @date 2023/3/22 18:53
 */
import java.lang.System;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.net.InetAddress;
 import java.net.Socket;
 import java.io.OutputStream ;
 import java.io.PrintStream;
 import java.io.InputStream;


         public class CallPython_Demo3 {
 public static void main(String[] args) {
         //System.out .println("Hello world!");
         // TODO Auto-generated method stub

         Socket socket = null;

         try {
            InetAddress addr = InetAddress.getLocalHost(); //因为都是在本机进行模拟,故也是用的getLoalHost()方法

             String host = addr.getHostName();
             String ip=addr.getHostAddress().toString(); //获取本机ip
             //log.info("调用远程接口:host=>"+ip+",port=>"+12345);

            //初始化套接字,设置访问服务的主机和进程端口号,HOST是访问python进程所在的主机名称(即远程服务器),可以是IP地址或者域名,PORT是python进程绑定的端口号

            socket = new Socket(host, 12345);

             // 获取服务进程的输出流对象
             OutputStream os = socket.getOutputStream();
             PrintStream out = new PrintStream(os);

             // 发送内容
             out.print("C:/Users/86151/Desktop/新建文件夹/20201106_215337.jpg");
            out.print("over"); // 这仅仅是为了标识一个信息的结束,“over”可以是自己任意设置的标识。告诉服务进程,内容发送完毕,可以开始处理


             // 获取服务进程的输入流对象
             InputStream is = socket.getInputStream();
             BufferedReader br = new BufferedReader(new InputStreamReader(is, "utf-8"));
             String tmp = null;
             StringBuilder sb = new StringBuilder();
            // 读取内容
             while ((tmp = br.readLine()) != null)
                 sb.append(tmp).append('\n');
             System.out.print(sb.toString());
             // 解析结果
             //JSONArray res = JSON.parseArray(sb.toString());
             } catch (IOException e) {
             e.printStackTrace();

             } finally {
             try {
                 if (socket != null)
                     socket.close();
                 } catch (IOException e) { }
             System.out.print("远程接口调用结束.");
             }

         }
 }

3、创建python文件写入如下代码

import socket
import sys
import threading

import PIL.ImageFilter
import numpy as np
from PIL import Image


# 自定义个线程类,继承Thread类。Thread对象控制线程有两种方法:(1)构造函数里传递进去一个可调用的对象;(2)子类重写run方法。本例采用第二种方法。

class ServerThreading(threading.Thread):

    # words = text2vec.load_lexicon()
    def __init__(self, clientsocket, recvsize=1024 * 1024, encoding="utf-8"):

        threading.Thread.__init__(self)
        self._socket = clientsocket
        self._recvsize = recvsize
        self._encoding = encoding
        pass

    # 重写Thread类的run()方法
    def run(self):
        print("开启线程.....")
        try:

            # 接受数据
            msg = ''
            while True:

                # 读取recvsize个字节
                rec = self._socket.recv(self._recvsize)  # 从套接字缓冲区接受指定大小字节的数据。

                # 解码
                msg += rec.decode(self._encoding)
                # 数据接收是否完毕,Python socket无法自己判断是否接收数据完毕,所以需要通过自定义协议标志数据接受完毕

                if msg.strip().endswith('over'):  # 这里的“over”是Java传递消息时会在结尾加上,这个自己任意定义。

                    print("删减标识符前%s" % msg)
                    msg = msg[:-4]  # 再把"over"四个字节去掉,之后才是真正的消息。
                    print("删减标识符后%s" % msg)
                    break

            # 比如,这里可以对传过来的数据做任何处理了
            # 比如1:打开图片
            sendmsg = Image.open(msg)  # 识别和打开一篇图片,采用lazy方式,只有处理时才真正打开。

            sendmsg.show()  # 真正打开图片
            # 比如2:
            # 对图片进行旋转,再保存到指定位置
            # sendmsg.rotate(90).show()

            # 比如3:
            # 对图片进行效果渲染,再保存到指定位置
            effect_niu = sendmsg.filter(PIL.ImageFilter.CONTOUR)
            effect_niu.show()
            effect_niu.save("data/niu_random_shift.jpg")  # 保存效果渲染之后的图片

            # a=sendmsg.copy()
            # .transform(size=sendmsg.size,method=Image.Transform.EXTENT).show()

            # 发送数据
            self._socket.send(("%s" % sendmsg).encode(self._encoding))  # send(): Send`data' to the server.

            pass
        except Exception as identifier:
            self._socket.send("500".encode(self._encoding))
            print(identifier)
            pass
        finally:
            self._socket.close()

        print("任务结束.....")

        pass

    def __del__(self):
        pass


def main():
    # 创建服务器套接字
    serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # socket.AF_INET时访问family种的一种,代表(TCP/IP – IPv4)。

    # 获取本地主机名称运行起来后,等待着连接:
    host = socket.gethostname()
    # 设置一个端口
    port = 12345
    # 将套接字与本地主机和端口绑定
    serversocket.bind((host, port))
    # 设置监听最大连接数
    serversocket.listen(5)
    # 获取本地服务器的连接信息
    myaddr = serversocket.getsockname()
    print("Python服务器地址:%s" % str(myaddr))  # 输出访问地址

    # 循环等待接收客户端信息
    while True:
        # 获取一个客户端连接
        clientsocket, addr = serversocket.accept()  # 进入监听状态,没有的话,就停止在这,直到监听到有访问请求。

        # serversocket.accept()方法 Wait for an incoming connection. Return a new socket representing the connection,

        # and the address of the client. For IP sockets, the address info is a pair(hostaddr, port).

        print("侦测到有访问请求,来源的clientsocket为:%s" % str(clientsocket))
        print("侦测到有访问请求,来源的address为:%s" % str(addr))

        try:
            t = ServerThreading(clientsocket=clientsocket)  # 为每一个请求开启一个处理线程
            t.start()  # 启动该线程的活动,每个线程最多在run()方法里调用一次。
            pass
        except Exception as identifier:
            print(identifier)
            pass
        pass

    serversocket.close()
    pass


if __name__ == '__main__':
    main()

注:修改代码中的图片的位置

可以发现图片传输成功,并出现渲染结果

完毕

物联沃分享整理
物联沃-IOTWORD物联网 » Java调用Python项目:传参、不传参和服务器部署详解

发表评论