java调用python脚本(传长参数)

java调用python脚本(传长参数)

背景

项目中需要使用java去调用python脚本并且需要传递一个包含图像信息的byte数组,这个数组长度为4,194,304.

java调用python无外乎两种方式

  1. jpython

    ​ jpython既可以在java中写python代码,也可以使用其提供的 PythonInterpreter 类来调用python脚本,但是无论是直接写python代码还是调用python脚本, Jython 都是 将Python语言实现为Java虚拟机(JVM)上的解释器的项目 ,而Jython 到目前为止只能支持python2 的语法和语义,并不支持Python 3语法。

  2. Runtime.getRuntime().exec(cmdArr) 或者 ProcessBuilder 模拟命令行调用

    ​ 这两种方式都是通过模拟命令行命令来调用python脚本的,一般情况下,我们可以把要传递的参数直接写在命令行中就可以了.但是我此次要传递的参数很长,写在命令行中执行时会报超长的异常.

解决方案

jpython不能兼容python3是硬伤,因此只能选择模拟命令行的方式调用,传参方式则使用获取进程的标准输出流,将参数写进流里进行传递(命令行调用python脚本会新起一个进程,即要使用这个进程的标准输出流).具体实现代码如下:

java部分代码

    public static ComputeResponse computeTest(ComputeRequest computeRequest) {
        JSONObject requestJson = new JSONObject();
        requestJson.put("rgbWidth", computeRequest.getRgbWidth());
        requestJson.put("rgbHeight", computeRequest.getRgbHeight());
        requestJson.put("rgbBands", computeRequest.getRgbBands());
        requestJson.put("rgbData", Arrays.toString(computeRequest.getRgbData()));
        requestJson.put("specWidth", computeRequest.getSpecWidth());
        requestJson.put("specHeight", computeRequest.getSpecHeight());
        requestJson.put("specBands", computeRequest.getSpecBands());
        requestJson.put("spectralData", Arrays.toString(computeRequest.getSpectralData());
        ComputeResponse response = ComputeResponse.builder()
                .success(false)
                .build();
        BufferedReader reader = null;
        try {
//            String[] cmdArr = new String[]{"python3", tobacEnginePath, requestJson.toJSONString()};
            String[] cmdArr = new String[]{"python3", "D:\\workspace\\CompanyProjects\\tobacco-impurity-detection\\script\\Demo.py"};
            boolean execResFlag = false;
            StringBuilder sb = null;
            int tryCount = 1;
            while (tryCount <= 5) {
                Process process = Runtime.getRuntime().exec(cmdArr);
                OutputStream outputStream = process.getOutputStream();
                outputStream.write(requestJson.toJSONString().getBytes(StandardCharsets.UTF_8));
                outputStream.flush();
                outputStream.close();

                sb = new StringBuilder();
                reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                String line = null;
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
                reader.close();

                // 0表示程序正常终止
                int execCode = process.waitFor();
                if (Objects.equals(0, execCode)) {
                    log.info("recive data:{}", sb);
                    break;
                }

                log.error("the [{}] time try call python script fail, exec code: {}", tryCount, execCode);
                tryCount++;
                ThreadUtils.sleep(20L);
            }

        } catch (Exception e) {
            log.error("Call python script fail: ", e);
            response.setSuccess(false);
        } finally {
            StreamUtils.close(reader);
        }
        return response;
    }


    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class ComputeRequest {
        private Integer rgbWidth;
        private Integer rgbHeight;
        private Integer rgbBands;
        private byte[] rgbData;

        private Integer specWidth = 1;
        private Integer specHeight = 1;
        private Integer specBands = 1;
        private float[] spectralData = new float[]{1,1,1};
    }

    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class ComputeResponse {
        private Boolean success;
        private String mess_isExist;
        private String mess_info;
        private String status;

    }

传参时并不是在String[] cmdArr中把参数也拼上使用命令行传参,而是先获取了进程的标准输出流OutputStream outputStream = process.getOutputStream();然后将参数写入到标准输出流中outputStream.write(requestJson.toJSONString().getBytes(StandardCharsets.*UTF_8*));,这样python脚本那边可以通过input_data = sys.stdin.read()的方式获取到参数.

注意
  • 写入流后要关闭outputStream ,不关闭的话python脚本那边会一直等待关闭命令而不往下执行.

  • 在给requestJson赋值byte[]时,把byte[]转成String格式,这点很重要,这是因为当在 Python 中接收到字节数组参数时,默认情况下,它们以字节的字符形式表示 ,而我们需要在python中也获取图像的字节数组才能对图像进行下一步操作,总而言之就是保证了我java传递的是什么,python就接收到的是什么

python代码

import json
import sys
import numpy as np
from matplotlib import pyplot as plt


if __name__ == '__main__':

    input_data = sys.stdin.read()

    # params = demjson3.decode(input_data)
    params = json.loads(input_data)
    rgbData_array = json.loads(params['rgbData'])

    temp1 = np.array(rgbData_array)
    rgbdata = np.reshape(temp1, (1024, 4096, 1))
    plt.imshow(rgbdata)
    plt.savefig('E:/temp/rgb.png')


    print(params['rgbData'])
注意
  • 将str解析成json时使用官方库的json.loads,不要使用demjson3.decode,效率天上地下的差距

  • java传参我们把数组转成了String的形式,python这边我们要用json.loads方法把str再转成array

这样,我们就可以在java调用python脚本的时候传递很长的参数了.当然解决方案不止此一种,比如我们可以保存临时文件,或者分批传参.但总体来说此种方案最优

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值