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

这篇文章主要介绍了如何利用SpringBoot实现简单的代码在线运行工具(类似于菜鸟工具),文中的示例代码讲解详细,需要的可以参考一下

说明

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

基本思路

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

后端实现

为了方便实现后端采用到了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 = os.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页面来进行测试

  Title 

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

最后,完善一下页面

  代码在线运行工具 

效果如下

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

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

赞(0) 打赏
未经允许不得转载:0133技术站首页 » Java