我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

基于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\class="lazy" data-src\main\resources\runCpp.bat
    c: F:\Spring\runCode\class="lazy" data-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;


@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 <stdio.h>\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;
    }
}

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页面来进行测试

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<select>
    <option selected>Java</option>
    <option>C</option>
</select>
<br/>
<textarea id="code" style="height: 500px; width: 600px"></textarea>
<button id="sub-btn" onclick="submit()">提交</button>
<br/>
<textarea id="output"></textarea>

<script>
    function submit() {
        let data = document.querySelector("#code").value;

        fetch("http://127.0.0.1:8848/run", {
            method: "POST",
            headers: {
                "Content-Type": "application/json; charset=UTF-8"
            },
            body: JSON.stringify({
                code: data,
                type: "JAVA"
            })

        }).then(response => response.json())
            .then(json => {
                console.log(json)
                document.querySelector("#output").value = json.output;
            });
    }
</script>
</body>
</html>

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

最后,完善一下页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>代码在线运行工具</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.min.css" rel="external nofollow"  integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <style>
        #editor {
            position: absolute;
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container">
        <a class="navbar-brand" href="/" rel="external nofollow" >代码在线运行工具</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
    </div>
</nav>
<div style="height: 30px"></div>
<div class="container shadow p-3 mb-5 bg-white rounded">
    <div class="container-fluid">
        <div class="row">
            <div class="col-2">
                <button id="sub-btn" class="btn btn-success " onclick="submit()">点击运行!</button>
            </div>
            <div class="col-3">
                <select onchange="selectLanguage(this)" id="language-type" class="form-control">
                    <option selected>Java</option>
                    <option>C</option>
                    <option>CPP</option>
                    <option>Python</option>
                </select>
            </div>
            <div class="col-3">
                <button type="button" class="btn btn-secondary" onclick="clean()">清空</button>
            </div>
        </div>
    </div>
    <div style="height: 20px"></div>

    <div class="row">
        <div class="col-7 border border-light">
            <div id="editor"></div>
        </div>
        <div class="col-1 border-left"></div>
        <div class="col text-center">
            <textarea id="output" class="form-control" rows="15"></textarea>
        </div>
    </div>
</div>
<script class="lazy" data-src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.8/ace.js" type="text/javascript"></script>
<script class="lazy" data-src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.8/ext-language_tools.min.js" type="text/javascript"></script>
<!--<script class="lazy" data-src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.8/mode-java.min.js" type="text/javascript"></script>-->
<script>
    ace.require("ace/ext/language_tools");
    const editor = ace.edit("editor");
    editor.session.setMode("ace/mode/java");
    editor.setTheme("ace/theme/github");
    // enable autocompletion and snippets
    editor.setOptions({
        enableBasicAutocompletion: true,
        enableSnippets: true,
        enableLiveAutocompletion: true
    });

    function submit() {
        document.querySelector("#output").value = "代码运行中!";
        let data = editor.getValue();


        fetch("http://127.0.0.1:8848/run", {
            method: "POST",
            headers: {
                "Content-Type": "application/json; charset=UTF-8"
            },
            body: JSON.stringify({
                code: data,
                type: document.querySelector("#language-type").value.toUpperCase()
            })

        }).then(response => response.json())
            .then(json => {
                console.log(json)
                document.querySelector("#output").value = json.output;
            });
    }

    function clean() {
        editor.setValue("");
    }

    function selectLanguage(e) {
        let mode = "ace/mode/" + e.value.toLowerCase();
        if (e.value.toLowerCase() === "c" || e.value.toLowerCase() === "cpp") {
            mode = "ace/mode/c_cpp"
        }
        editor.session.setMode(mode);
    }
</script>
</body>
</html>

效果如下

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

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

基于Next.js实现在线Excel的详细代码

Next.js是一款React开发框架,它可以帮助我们构建React应用程序。作为一个轻量级React服务端渲染应用框架,这篇文章主要介绍了基于 Next.js实现在线Excel,需要的朋友可以参考下
2022-11-13

基于Java实现进制转换工具类的示例代码

这篇文章主要为大家详细介绍了如何基于Java实现进制转换工具类,从而实现减少参数长度的效果,文中的示例代码讲解详细,需要的可以参考一下
2023-02-19

Shell脚本实现的基于SVN的代码提交量统计工具

最近没啥事,就用bash写了一个基于svn的代码统计小工具。 可以指定统计的目录,默认递归统计子目录。 目前还没有屏蔽指定目录的功能。哈 代码比较粗糙。不过先晒出来。#!/bin/bash - #""""""""""""""""""""
2022-06-04

基于SpringBoot实现邮箱找回密码的代码示例

本文介绍了如何使用SpringBoot实现邮件找回密码功能,包括配置邮件服务器、创建用户实体、生成密码重置令牌、发送电子邮件、验证令牌和更新用户密码等步骤。文中提供了详细的代码示例,涵盖了用户实体类、密码重置服务和控制器等关键组件。通过遵循这些步骤,开发者可以轻松地在SpringBoot应用程序中实现邮箱找回密码功能。
基于SpringBoot实现邮箱找回密码的代码示例
2024-04-02

在Android线程池里运行代码任务实例

本节展示如何在线程池里执行任务。流程是,添加一个任务到线程池的工作队列,当有线程可用时(执行完其他任务,空闲,或者还没执行任务),ThreadPoolExecutor会从队列里取任务,并在线程里运行。 本课同时向你展示了如何停止正在运行的任
2022-06-06

如何实现Shell脚本基于SVN的代码提交量统计工具

这篇文章主要讲解了“如何实现Shell脚本基于SVN的代码提交量统计工具”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何实现Shell脚本基于SVN的代码提交量统计工具”吧!#!/bin/
2023-06-09

基于Python实现模拟三体运动的示例代码

此前所做的一切三体和太阳系的动画,都是基于牛顿力学的,而且直接对微分进行差分化,从而精度非常感人,用不了几年就得撞一起去。所以本文来用Python重新模拟一下三体运动,感兴趣的可以了解一下
2023-03-10

基于Java实现的大乐透号码生成器工具类

大乐透是中国体育彩票的一种玩法,是国家体育总局体彩中心为适应市场发展需要。本文为大家准备了一个大乐透号码生成器工具类,感兴趣的可以了解一下
2022-11-13

关于Java利用反射实现动态运行一行或多行代码

这篇文章主要介绍了关于Java利用反射实现动态运行一行或多行代码,借鉴了别人的方法和书上的内容,最后将题目完成了,和大家一起分享以下解决方法,需要的朋友可以参考下
2023-05-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录