基于SpringBoot实现代码在线运行工具

网友投稿 530 2022-07-26


目录说明基本思路后端实现前端

说明

由于没有实现沙盒,所以这个运行只适合提交自己写的代码到服务器,不适合像 菜鸟工具 那样可以让人公开提交代码并访问。

基本思路

前端提交代码,后端运行并返回结果。

后端实现

为了方便实现后端采用到了SpringBoot

我们需要先完成代码运行所需要的配置

@ConfigurationProperties(prefix = "run.script")

@Component

public class Config {

private String cpp;

private String c;

private String python;

public void setCpp(String cpp) {

this.cpp = cpp;

}

public void setC(String c) {

this.c = c;

}

public void setPython(String python) {

this.python = python;

}

public String getCpp() {

return cpp;

}

public String getC() {

return c;

}

public String getPython() {

return python;

}

}

配置yml文件

此处的cpp和c应为需要编译运行,所以需要根据不同的操作系统写运行脚本

所有的路径都必须是绝对路径

run:

script:

cpp: F:\Spring\runCode\src\main\resources\runCpp.bat

c: F:\Spring\runCode\src\main\resources\runC.bat

python: C:\Users\puzhiwei\AppData\Local\Programs\Python\Python38\python.exe

然后我们需要将前端提交的代码保存到文件

// 获取系统缓存文件的位置

String tmpDir = System.getProperty("java.io.tmpdir");

// 随机文件夹的名字

File pwd = Paths.get(tmpDir, String.format("%016x", nextLong.incrementAndGet())).toFile();

// 新建文件夹

pwd.mkdirs();

ProcessBuilder pb = null;

switch (type) {

case "C":

try (Writer writer = new BufferedWriter(new FileWriter(new File(pwd, "Main.c"), Charset.defaultCharset()))) {

writer.write(code);

}

pb = new ProcessBuilder().command(config.getC()).directory(pwd);

break;

case "CPP":

try (Writer writer = new BufferedWriter(new FileWriter(new File(pwd, "Main.cpp"), Charset.defaultCharset()))) {

writer.write(code);

}

pb = new ProcessBuilder().command(config.getCpp()).directory(pwd);

break;

case "JAVA":

try (Writer writer = new BufferedWriter(new FileWriter(new File(pwd, "Main.java"), Charset.defaultCharset()))) {

writer.write(code);

}

String[] command = new String[]{getJavaExecutePath(), "-Dfile.encoding=" + Charset.defaultCharset(), "--source", "11", "--enable-preview", "Main.java"};

pb = new ProcessBuilder().command(command).directory(pwd);

break;

case "PYTHON":

try (Writer writer = new BufferedWriter(new FileWriter(new File(pwd, "Main.py"), Charset.defaultCharset()))) {

writer.write(code);

}

pb = new ProcessBuilder().command(config.getPython(), "Main.py").directory(pwd);

break;

default:

break;

}

这段代码主要实现了将代码保存到系统的缓存文件夹中,

pb为要在终端中执行的编译运行命令

由于C和C++需要编译才能执行,所以执行的是运行脚本,需要根据自己的系统进行修改

在windows下如下

@echo off

clang -std=c11 main.c && a.exe

@echo off

clang++ -std=c++17 main.cpp && a.exe

获取Java执行路径的的代码如下

private String getJavaExecutePath() {

if (javaExec == null) {

String javaHome = System.getProperty("java.home");

String os = System.getProperty("os.name");

boolean isWindows = oshttp://.toLowerCase().startsWith("windows");

Path javaPath = Paths.get(javaHome, "bin", isWindows ? "java.exe" : "java");

javaExec = javaPath.toString();

}

return javaExec;

}

之后就是使用 ProcessBuilder 执行脚本,并读取运行结果了

pb.redirectErrorStream(true);

Process p = pb.start();

if (p.waitFor(5, TimeUnit.SECONDS)) {

String result = null;

try (InputStream input = p.getInputStream()) {

result = readAsString(input, Charset.defaultCharset());

}

return new ProcessResult(p.exitValue(), result);

} else {

System.err.println(String.format("Error: process %s timeout. destroy forcibly.", p.pid()));

p.destroyForcibly();

return new ProcessResult(p.exitValue(), "运行超时");

}

最后,这个类的完整代码如下

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import java.io.*;

import java.nio.charset.Charset;

import java.nio.file.Path;

import java.nio.file.Paths;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.atomic.AtomicLong;

/**

* @author Pu Zhiwei {@literal puzhiweipuzhiwei@foxmail.com}

* create 2020-03-13 18:22

*/

@Component

public class RunCode {

private final Config config;

private static String javaExec = null;

private static AtomicLong nextLong = new AtomicLong(System.currentTimeMillis());

@Autowired

public RunCode(Config config) {

this.config = config;

}

public ProcessResult runCode(String type, String code) throws IOException, InterruptedException {

// 获取系统缓存文件的位置

String tmpDir = System.getProperty("java.io.tmpdir");

// 随机文件夹的名字

File pwd = Paths.get(tmpDir, String.format("%016x", nextLong.incrementAndGet())).toFile();

// 新建文件夹

pwd.mkdirs();

ProcessBuilder pb = null;

switch (type) {

case "C":

try (Writer writer = new BufferedWriter(new FileWriter(new File(pwd, "Main.c"), Charset.defaultCharset()))) {

writer.write(code);

}

pb = new ProcessBuilder().command(config.getC()).directory(pwd);

break;

case "CPP":

try (Writer writer = new BufferedWriter(new FileWriter(new File(pwd, "Main.cpp"), Charset.defaultCharset()))) {

writer.write(code);

}

pb = new ProcessBuilder().command(config.getCpp()).directory(pwd);

break;

case "JAVA":

try (Writer writer = new BufferedWriter(new FileWriter(new File(pwd, "Main.java"), Charset.defaultCharset()))) {

writer.write(code);

}

String[] command = new String[]{getJavaExecutePath(), "-Dfile.encoding=" + Charset.defaultCharset(), "--source", "11", "--enable-preview", "Main.java"};

pb = new ProcessBuilder().command(command).directory(pwd);

break;

case "PYTHON":

try (Writer writer = new BufferedWriter(new FileWriter(new File(pwd, "Main.py"), Charset.defaultCharset()))) {

writer.write(code);

}

pb = new ProcessBuilder().command(config.getPython(), "Main.py").directory(pwd);

break;

default:

break;

}

pb.redirectErrorStream(true);

Process p = pb.start();

if (p.waitFor(5, TimeUnit.SECONDS)) {

String result = null;

try (InputStream input = p.getInputStream()) {

result = readAsString(input, Charset.defaultCharset());

}

return new ProcessResult(p.exitValue(), result);

} else {

System.err.println(String.format("Error: process %s timeout. destroy forcibly.", p.pid()));

p.destroyForcibly();

return new ProcessResult(p.exitValue(), "运行超时");

}

}

private String getJavaExecutePath() {

if (javaExec == null) {

String javaHome = System.getProperty("java.home");

String os = System.getProperty("os.name");

boolean isWindows = os.toLowerCase().startsWith("windows");

Path javaPath = Paths.get(javaHome, "bin", isWindows ? "java.exe" : "java");

javaExec = javaPath.toString();

}

return javaExec;

}

public String readAsString(InputStream input, Charset charset) throws IOException {

ByteArrayOutputStream output = new ByteArrayOutputStream();

byte[] buffer = new byte[102400];

for (; ; ) {

int n = input.read(buffer);

if (n == (-1)) {

break;

}

output.write(buffer, 0, n);

}

return output.toString(charset);

}

}

写完这些,我们就基本完成了代码在后端的运行并返回结果

接下来可以写一个测试方法测试一下结果的运行

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest

class RunApplicationTests {

@Autowired

private RunCode runCode;

@Test

void contextLoads() throws Exception {

String code = "#include \n" +

"\n" +

"int main()\n" +

"{\n" +

" printf(\"Hello, World! \\n\");\n" +

" \n" +

" return 0;\n" +

"}";

System.out.println(runCode.runCode("C", code).getOutput());

}

}

如果没有异常,应该可以看到如下内容

最后,写一个controller,用来接收前端提交的代码

@RestController

@CrossOrigin("*")

public class WebController {

public final RunCode runCode;

@Autowired

public WebController(RunCode runCode) {

this.runCode = runCode;

}

@PostMapping("/run")

public ProcessResult runCode(@RequestBody CodeModel codeModel) throws Exception {

return runCode.runCode(codeModel.getType(), codeModel.getCode());

}

}

public class CodeModel {

private String type;

private String code;

public String getType() {

return type;

}

public void setType(String type) {

this.type = type;

}

public String getCode() {

return code;

}

public void setCode(String code) {

this.code = code;

}

}

/**

* @author Pu Zhiwei {@literal puzhiweipuzhiwei@foxmail.com}

* create 2020-03-13 18:26

*/

public class ProcessResult {

private int exitCode;

private String output;

public ProcessResult(int exitCode, String output) {

this.exitCode = exitCode;

this.output = output;

}

public int getExitCode() {

return exitCode;

}

public String getOutput() {

return output;

}

}

至此,我们的后端就基本完成了。

前端

我们先写一个简单的html页面来进行测试



如果没有问题,我们就能看到如下结果了

最后,完善一下页面

代码在线运行工具

效果如下

以上就是基于SpringBoot实现代码在线运行工具的详细内容,更多关于SpringBoot代码在线运行工具的资料请关注我们其它相关文章!


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java超详细分析讲解final关键字的用法
下一篇:springboot之Jpa通用接口及公共方法使用示例
相关文章

 发表评论

暂时没有评论,来抢沙发吧~