有一个特殊需求,要求网站在线执行代码。于是就捣鼓了一下:
这个是python的:
首先把源代码生成文件:
private boolean codeToFile() { long t = new Date().getTime(); this.tempFile = this.outDir + "/" + "p" + t + ".py"; File file = new File(tempFile); try { if (file.createNewFile()) { FileOutputStream f1 = new FileOutputStream(file);//(file,true),这里有true的话,代表可以在文件后面追加内容 byte[] buff = code.getBytes();//将字符串转换为字节数组 try { f1.write(buff);//把字节数组的内容写进去文件 } catch (Exception e) { // TODO: handle exception } finally { try { f1.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } } } } catch (IOException e) { e.printStackTrace(); log.info(this.tempFile + "创建失败!"); this.tempFile = ""; return false; } return true; }
然后执行代码,里面的this.temp就是上面这个函数生成的,带路径哦:
public void execPython() throws IOException { // log.info("-----输出-----"); //Runtime run = Runtime.getRuntime(); BufferedReader in = null; BufferedReader err = null; // Process p = null; //InputStream in = null; //BufferedReader br = null; try { long time = new Date().getTime(); log.info("将要执行的代码:\n" + this.code);
process = Runtime.getRuntime().exec("python.exe " + this.tempFile);
//特别注意,in是代码的输出,out是输入 in = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)); err = new BufferedReader(new InputStreamReader(process.getErrorStream(), StandardCharsets.UTF_8)); BufferedWriter out = new BufferedWriter(new OutputStreamWriter(process.getOutputStream())); if (!Common.isEmpty(this.testArray)) { for (int i = 0; i < testArray.size(); i++) { out.write(testArray.get(i) + "\n"); } out.flush(); out.close();//关闭流 } else { out.write("24\n48 36 72 108 12 24\n");//输入一些数据,防止等待输入的堵塞 out.flush(); out.close();//关闭流 } StringBuffer outputBuffer = new StringBuffer(); boolean deadLoop = false; synchronized (in) { in.wait(500); } //s=br.readLine(); // 防止没有输出的死循环 String s; if (false) { output = "没有输出或出现死循环"; deadLoop = true; } else { long now = new Date().getTime(); while ((s = in.readLine()) != null) { // log.info(lineTxt); log.info("输出结果为:" + s); outputBuffer.append(s); outputBuffer.append("@"); // 防止死循环,只能防止有输出的死循环 if (outputBuffer.length() >= 2000 || now - time >= 1000 * 60 * 2) { log.error(process.toString()); deadLoop = true; break; } } } getSyntaxError(err); //out.flush(); //out.close();//关闭流 log.info("============result:==============="); if (!deadLoop) { output = outputBuffer.toString().trim(); output = output.length() > 1 ? output.substring(0, output.length() - 1) : ""; } else { output = "没有输出或出现死循环"; } log.info(output); int exitCode = process.waitFor(); System.out.println("Python脚本执行完毕,退出码: " + exitCode); } catch (IOException | InterruptedException e) { e.printStackTrace(); } finally { if (err != null) err.close(); if (in != null) in.close(); if (process != null) { process.destroyForcibly(); process.destroy(); }
log.info("删除临时文件" + this.tempFile); DeleteFileUtil.delete(new StringBuilder(this.tempFile).toString());
} }
这样运行大部分程序都可以,也可以防止某些死循环(后面发现这实在是一个坑)。
但是代码中要是有输入的语句例如input,那么在获得输出的代码那里就会阻塞,也就是这里:while ((s = in.readLine()) != null)
要解决这个问题,必须多线程,把输入的部分另开一条线程。如果嫌麻烦,直接在监测代码,如果包含input,就不执行了,简单粗暴。如果非要首先输入的功能,那实在是挺麻烦的。我是这么做的。
首先把输入流搞一个类:
public class ProcessInput implements Runnable { private Process process; private String input; public ProcessInput(Process process) { this.process = process; } public void setInput(String input) { this.input = input; } @Override public void run() { System.out.println("ProcessInput run"); try { while (true) { if (input != null) { System.out.println("接收到:"+input); process.getOutputStream().write(input.getBytes()); process.getOutputStream().write(System.lineSeparator().getBytes()); process.getOutputStream().flush(); input = null; } Thread.sleep(500); System.out.println(" waiting..."); if(!process.isAlive()){ break; } } } catch (Exception e) { e.printStackTrace(); } } }
然后这样启动:
private ProcessInput input; private ProcessInput initInputProcess() throws InterruptedException { processInput = new ProcessInput(process); processInputThread = new Thread(processInput); processInputThread.setName(process+"_python_processInputThread"); processInputThread.start(); processInputThread.join(); if (processInput != null) { System.out.println("t1 run " + processInput); } else System.out.println("t1 run"); return processInput; }
而接收用户输入的那端:
@PostMapping("/processInput") public CommonMessage processInput() throws IOException, InterruptedException { CommonMessage message = new CommonMessage(); String inputString = request.getParameter("input"); if (input == null) { message.setInfo("当前没有要输入的进程"); message.setData(""); } else if (pythonRunner.getProcess().isAlive()) { input.setInput(inputString); Thread.sleep(2000); message.setData(pythonRunner.result); } else { message.setInfo("当前没有要输入的进程"); } if (!process.isAlive()) pythonRunner.getPythonCompile().getProcessInputThread().interrupt(); return message; } 用户输入的东西再给到input