之前的临时文件都是存储在tmp目录下的.当多个请求并发进行的时候, 可能就分不清哪个请求对应哪个文件了.
解决办法: 使用 Java 中的 UUID
这个类就能生成一个 UUID了
采用构造方法的方式, 每次调用 Task 类的时候就生成一个 UUID ,这个UUID就是存放这些文件的上级目录.
public class Task {
// 约定临时文件所在的目录
private String WORK_DIR = null;
// 约定代码的类名
private String CLASS = null;
// 约定要编译的代码文件名
private String CODE = null;
// 约定存放编译错误信息的文件名
private String COMPILE_ERROR = null;
// 约定存放运行时的标准输出的文件名
private String STDOUT = null;
// 约定存放运行时的标准错误的文件名
private String STDERR = null;
public Task() {
WORK_DIR = "./tmp/" + UUID.randomUUID().toString() + "/";
CLASS = "Solution";
CODE = WORK_DIR + "Solution.java";
COMPILE_ERROR = WORK_DIR + "compileError.txt";
STDOUT = WORK_DIR + "stdout.txt";
STDERR = WORK_DIR + "stderr.txt";
}
// ....以下内容不变
}
使用 System.getProperty("user.dir")
这个方法可以获取当前的工作目录
将这段代码加入到 ResultServlet 中
启动服务器, 并使用postman发送一个请求
这里我的前端模板是从网上下载过来的. 然后保留需要的地方, 不需要的就删除了.
查看主要代码
这里主要的思路就是访问主页的时候, 进行交互1, 读取题目列表页, 然后构建表格.
之前约定的是 GET 请求, 交互的mehod为problem.
<script>
function getProblem(){
$.ajax({
url: "problem",
method: "GET",
success: function(data,status){
makeProblem(data);
}
})
}
function makeProblem(problems){
let tbody = document.querySelector('.tb-body');
for(let problem of problems){
// 创建 tr
let tr = document.createElement('tr');
// 创建 Id列的td
let tdId = document.createElement('td');
// 插入响应中的id
tdId.innerHTML = problem.id;
tr.appendChild(tdId);
// 创建 level列的td
let tdLevel = document.createElement('td');
// 创建 响应中的难度
tdLevel.innerHTML = problem.level;
tr.appendChild(tdLevel);
// 创建 title列的td
let td = document.createElement('td');
// 创建 超链接
let tdTitle = document.createElement('a');
// 内容为 响应的标题
tdTitle.innerHTML = problem.title;
// 这里设置这个题号链接(带上id)
tdTitle.href = "details.html?id=" + problem.id;
// _block是跳转新页面
tdTitle.target = "_block";
td.appendChild(tdTitle);
tr.appendChild(td);
tbody.appendChild(tr);
}
}
getProblem();
</script>
这个界面主要就是加载对应id的题目的内容. 并有一输入框, 提交按钮, 以及对应的结果展示.
这里是 GET请求, method为 desc. 但是desc后面会带id
js中 可以使用 location.search
来表示 ?id=1
这部分
function getProblem() {
$.ajax({
url: "desc" +location.search,
method: "GET",
success: function(data,status){
loading(data);
}
})
}
function loading(problem){
// 获取详情页的方框
let desc = document.querySelector("#myleft");
// 创建一个h2大小的标题
let h = document.createElement("h2");
// 这个标题 展示 id 和 题目名称
h.innerHTML = problem.id+"."+problem.title;
desc.appendChild(h);
// 创建一个p
let level = document.createElement('p');
// 这一段放等级
level.innerHTML = problem.level;
// 根据等级来展现字体颜色.
if(problem.level == "简单"){
level.style = "color:rgb(48, 221, 32)";
}else if(problem.level == "困难"){
level.style = "color:red";
}else{
level.style = "color:gold";
}
desc.appendChild(level);
// 由于直接使用p标签 无法完全正常换行.就外面套一层pre
let pre = document.createElement('pre');
let p = document.createElement('p');
p.innerHTML = problem.description;
pre.appendChild(p);
desc.appendChild(pre);
// 把约定好的代码放入到输入框中
let codeEditor = document.querySelector("#codeEditor");
codeEditor.innerHTML = problem.templateCode;
再点击按钮之后, 会有一个 post 请求, method为result.,将响应的内容写入结果框中.
继续在刚刚的交互2中写入以下代码
let submitButton = document.querySelector('.mysubmit');
submitButton.onclick = function() {
let body = {
id: problem.id,
code: problem.code,
};
$.ajax({
url: "result",
method: "post",
data: JSON.stringify(body),
success: function(data,status){
let result = document.querySelector("#result");
if(data.error == 0){
// 编译运行成功
console.log("编译运行成功");
result.innerHTML = data.stdout;
}else{
console.log("编译运行失败");
result.innerHTML = data.reason;
}
}
})
}
这里我们发现 这里的代码输入框非常不好用.
可以采用前端的 ace 库
<script src="https://cdn.bootcss.com/ace/1.2.9/ace.js"></script>
<script src="https://cdn.bootcss.com/ace/1.2.9/ext-language_tools.js"></script>
<script>
function initAce() {
// 参数 editor 就对应到刚才在 html 里加的那个 div 的 id
let editor = ace.edit("editor");
editor.setOptions({
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true
});
// 这是设置背景主题的
editor.setTheme("ace/theme/twilight");
// 这是设置代码的语言的
editor.session.setMode("ace/mode/java");
editor.resize();
document.getElementById('editor').style.fontSize = '20px';
return editor;
}
let editor = initAce();
由于你不知道你的用户是使用什么代码, 可能运行会制造一些危险代码.
如: Runtime.getRuntime().exec("rm -rf /");
就可能把服务器上东西都删了
那么为了防止这样的情况
可以提前将危险代码记录下来, 然后在提交的时候, 比较以下这个代码, 如果是危险代码, 就直接返回错误.
private boolean checkCodeSafe(String code) {
List<String> list = new ArrayList<>();
// 防止提交的代码运行恶意程序
list.add("Runtime");
list.add("exec");
// 禁止提交的代码读写文件
list.add("java.io");
// 禁止提交的代码访问网络
list.add("java.net");
for (String str : list){
int post = code.indexOf(str);
if (post >= 0){
return false;
}
}
return true;
}
// 安全性判定
if (!checkCodeSafe(question.getCode())){
System.out.println("用户提交了不安全的代码");
answer.setError(3);
answer.setReason("您提交的代码不安全, 危害了服务器!");
return answer;
}