基于SpringBoot实现代码在线运行工具
目录
- 说明
- 基本思路
- 后端实现
- 前端
说明 由于没有实现沙盒,所以这个运行只适合提交自己写的代码到服务器,不适合像 菜鸟工具 那样可以让人公开提交代码并访问。
基本思路 前端提交代码,后端运行并返回结果。
后端实现 为了方便实现后端采用到了SpringBoot
我们需要先完成代码运行所需要的配置
@ConfigurationProperties(prefix = "run.script")@Componentpublic 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.batc: F:\Spring\runCode\src\main\resources\runC.batpython: 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 offclang -std=c11 main.c && a.exe
@echo offclang++ -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} * create2020-03-13 18:22 */@Componentpublic class RunCode {private final Config config; private static String javaExec = null; private static AtomicLong nextLong = new AtomicLong(System.currentTimeMillis()); @Autowiredpublic 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; @SpringBootTestclass RunApplicationTests {@Autowiredprivate RunCode runCode; @Testvoid 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; @Autowiredpublic 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} * create2020-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 - 锐客网
如果没有问题,我们就能看到如下结果了
文章图片
最后,完善一下页面
代码在线运行工具 - 锐客网 #editor {position: absolute; width: 100%; height: 100%; }
效果如下
文章图片
文章图片
文章图片
【基于SpringBoot实现代码在线运行工具】以上就是基于SpringBoot实现代码在线运行工具的详细内容,更多关于SpringBoot代码在线运行工具的资料请关注脚本之家其它相关文章!
推荐阅读
- #|【Spring Cloud】新闻头条微服务项目(FreeMarker模板引擎实现文章静态页面生成)
- Java|基于SpringCloud Alibaba的微服务项目基础骨架
- 2022秋招前端面试题(五)(附答案)
- VUE脚手架框架编写简洁的登录界面的实现
- Java实现文件批量重命名|Java实现文件批量重命名,移动和删除
- Java通过自定义类加载器实现类隔离
- 机器学习算法|K-means聚类算法原理及python具体实现
- 基于图像二维熵的视频信号丢失检测(Signal|基于图像二维熵的视频信号丢失检测(Signal Loss Detection)
- 水下机器人|基于ROS搭建简易软件框架实现ROV水下目标跟踪(补3)--ROS2.0
- Unity总结|Unity实现玩家角色移动控制——CharacterController组件