最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于SpringBoot實(shí)現(xiàn)代碼在線運(yùn)行工具

 更新時(shí)間:2022年06月08日 09:45:08   作者:陜西顏值扛把子  
這篇文章主要介紹了如何利用SpringBoot實(shí)現(xiàn)簡(jiǎn)單的代碼在線運(yùn)行工具(類似于菜鳥(niǎo)工具),文中的示例代碼講解詳細(xì),需要的可以參考一下

說(shuō)明

由于沒(méi)有實(shí)現(xiàn)沙盒,所以這個(gè)運(yùn)行只適合提交自己寫的代碼到服務(wù)器,不適合像 菜鳥(niǎo)工具 那樣可以讓人公開(kāi)提交代碼并訪問(wèn)。

基本思路

前端提交代碼,后端運(yùn)行并返回結(jié)果。

后端實(shí)現(xiàn)

為了方便實(shí)現(xiàn)后端采用到了SpringBoot

我們需要先完成代碼運(yùn)行所需要的配置

@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應(yīng)為需要編譯運(yùn)行,所以需要根據(jù)不同的操作系統(tǒng)寫運(yùn)行腳本

所有的路徑都必須是絕對(duì)路徑

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

然后我們需要將前端提交的代碼保存到文件

// 獲取系統(tǒng)緩存文件的位置
        String tmpDir = System.getProperty("java.io.tmpdir");
        // 隨機(jī)文件夾的名字
        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;
        }

這段代碼主要實(shí)現(xiàn)了將代碼保存到系統(tǒng)的緩存文件夾中,

pb為要在終端中執(zhí)行的編譯運(yùn)行命令

由于C和C++需要編譯才能執(zhí)行,所以執(zhí)行的是運(yùn)行腳本,需要根據(jù)自己的系統(tǒng)進(jìn)行修改

在windows下如下

@echo off
clang -std=c11 main.c && a.exe
@echo off
clang++ -std=c++17 main.cpp && a.exe

獲取Java執(zhí)行路徑的的代碼如下

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 執(zhí)行腳本,并讀取運(yùn)行結(jié)果了

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(), "運(yùn)行超時(shí)");
        }

最后,這個(gè)類的完整代碼如下

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 {
        // 獲取系統(tǒng)緩存文件的位置
        String tmpDir = System.getProperty("java.io.tmpdir");
        // 隨機(jī)文件夾的名字
        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(), "運(yùn)行超時(shí)");
        }
    }



    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);
    }
}

寫完這些,我們就基本完成了代碼在后端的運(yùn)行并返回結(jié)果

接下來(lái)可以寫一個(gè)測(cè)試方法測(cè)試一下結(jié)果的運(yùn)行

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());
    }

}

如果沒(méi)有異常,應(yīng)該可以看到如下內(nèi)容

最后,寫一個(gè)controller,用來(lái)接收前端提交的代碼

@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;
    }
}

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

前端

我們先寫一個(gè)簡(jiǎn)單的html頁(yè)面來(lái)進(jìn)行測(cè)試

<!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>

如果沒(méi)有問(wèn)題,我們就能看到如下結(jié)果了

最后,完善一下頁(yè)面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>代碼在線運(yùn)行工具</title>
    <link rel="stylesheet"  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" >代碼在線運(yùn)行工具</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()">點(diǎn)擊運(yùn)行!</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 src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.8/ace.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.8/ext-language_tools.min.js" type="text/javascript"></script>
<!--<script 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 = "代碼運(yùn)行中!";
        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實(shí)現(xiàn)代碼在線運(yùn)行工具的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot代碼在線運(yùn)行工具的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot整合Minio的過(guò)程(支持公有及私有bucket)

    SpringBoot整合Minio的過(guò)程(支持公有及私有bucket)

    Bucket 是存儲(chǔ)Object的邏輯空間,每個(gè)Bucket之間的數(shù)據(jù)是相互隔離的,對(duì)用戶而言,相當(dāng)于存放文件的頂層文件夾,這篇文章主要介紹了SpringBoot整合Minio的過(guò)程(支持公有及私有bucket),需要的朋友可以參考下
    2025-03-03
  • Java ffmpeg 實(shí)現(xiàn)視頻加文字/圖片水印功能(示例代碼)

    Java ffmpeg 實(shí)現(xiàn)視頻加文字/圖片水印功能(示例代碼)

    本文介紹了使用Java和ffmpeg庫(kù)實(shí)現(xiàn)視頻加文字或圖片水印的方法,通過(guò)引入依賴代碼和示例,詳細(xì)說(shuō)明了如何將文字水印和圖片水印添加到視頻中,為需要在視頻中加入水印的開(kāi)發(fā)者提供了實(shí)用的指導(dǎo),這種方法不僅增強(qiáng)了視頻內(nèi)容的版權(quán)保護(hù),也為視頻編輯提供了更多的可能性
    2024-10-10
  • Springboot如何統(tǒng)一處理Filter異常

    Springboot如何統(tǒng)一處理Filter異常

    這篇文章主要介紹了Springboot如何統(tǒng)一處理Filter異常問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Java中的多態(tài)用法實(shí)例分析

    Java中的多態(tài)用法實(shí)例分析

    這篇文章主要介紹了Java中的多態(tài)用法,較為詳細(xì)的分析了java中多態(tài)的概念與相關(guān)的實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-04-04
  • Springboot日志配置的實(shí)現(xiàn)示例

    Springboot日志配置的實(shí)現(xiàn)示例

    本文主要介紹了Springboot日志配置的實(shí)現(xiàn)示例,使用slf4j和logback的方式記錄日志,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08
  • Java生成Jar包方法步驟

    Java生成Jar包方法步驟

    在Java開(kāi)發(fā)中,打包成JAR文件是一種常見(jiàn)的方式,本文主要介紹了Java生成Jar包方法步驟,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-10-10
  • Java類的繼承實(shí)例詳解(動(dòng)力節(jié)點(diǎn)Java學(xué)院整理)

    Java類的繼承實(shí)例詳解(動(dòng)力節(jié)點(diǎn)Java學(xué)院整理)

    在Java開(kāi)發(fā)中,我們常常用到繼承這一概念,可以說(shuō)繼承是Java這類面向?qū)ο缶幊陶Z(yǔ)言的基石,今天小編一起和大家一起學(xué)習(xí)java類的繼承
    2017-04-04
  • Maven構(gòu)建生命周期詳細(xì)介紹

    Maven構(gòu)建生命周期詳細(xì)介紹

    這篇文章主要介紹了Maven構(gòu)建生命周期詳細(xì)介紹,小編覺(jué)得還是挺不錯(cuò)的,這里分享給大家,需要的朋友可以參考下。
    2017-11-11
  • Spring BeanPostProcessor源碼示例解析

    Spring BeanPostProcessor源碼示例解析

    這篇文章主要為大家介紹了Spring BeanPostProcessor源碼示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • spring注入在有常量的情況下使用@AllArgsConstructor操作

    spring注入在有常量的情況下使用@AllArgsConstructor操作

    這篇文章主要介紹了spring注入在有常量的情況下使用@AllArgsConstructor操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09

最新評(píng)論

崇义县| 涟水县| 柘城县| 武汉市| 景泰县| 刚察县| 腾冲县| 化德县| 偃师市| 宜兰市| 霍林郭勒市| 五大连池市| 九寨沟县| 吉林市| 祥云县| 夏津县| 吉林省| 洞口县| 兰坪| 浦东新区| 西充县| 灌阳县| 兴业县| 沙坪坝区| 石屏县| 广宗县| 隆尧县| 右玉县| 喀喇沁旗| 石首市| 光山县| 依兰县| 浦东新区| 怀仁县| 温泉县| 曲周县| 潍坊市| 成都市| 福鼎市| 建德市| 建昌县|