在Vue和Spring Boot项目中,我们可以使用ProcessBuilder来实现类似终端的功能。该功能可以让用户在Web界面上执行操作系统命令,并查看命令的输出结果。
下面是一份完整的实现流程:
一.前端代码
前端页面通过Vue框架实现,它包含一个文本域和一个文本框。用户可以在文本框中输入命令,并回车执行该命令。执行结果将显示在页面上。按上下键可查看历史命令
特殊命令使用:
clear :清除文本域和文本框内容
select file : 用于打开文件选择器,并选取文件
upload file: 执行该命令将会将select file 选取的文件进行上传
download file -name: 执行该命令将会下载指定文件
bash:切换执行环境
<template>
<div>
<div class="terminal">
<textarea class="output" ref="terminalOutput" v-model="outputContent" readonly></textarea>
<div class="input-line">
<span style="font-size: 16px;">console {{ currentDirectory }} -></span>
<input v-model="command" spellcheck="false" @keyup.up="previousCommand" @keyup.down="nextCommand"
@keyup.enter="executeCommand" ref="inputBox" />
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
command: '',
outputContent: '',
commandHistory: [],
historyIndex: -1,
currentDirectory: '/root',
currentTmpDirectory: '/root',
charsetName: '', // 控制台字符集
timeOut: 10,
isBash: false,
selectedFile: null,
};
},
created() {
},
mounted() {
this.openFileManager()
},
methods: {
executeCommand() {
if (!this.watchCommand(this.command)) {
return;
}
// 拼接路径,用于获取执行目录
if (this.command.trim().toLowerCase().startsWith("cd ")) {
this.changeDirectory(this.command);
} else {
this.currentTmpDirectory = this.currentDirectory
}
axios.post('/api/execute-command', {
command: this.command, directory: this.currentTmpDirectory,
timeOut: this.timeOut, charsetName: this.charsetName,
isBash: this.isBash
}, {
headers: { 'Content-Type': 'application/json' }
})
.then(response => {
const { output, currentDirectory } = response.data
this.outputContent += `\nconsole -> ${this.command}\n${output}\n`;
this.commandHistory.push(this.command);
if (currentDirectory && response.data != 'Path does not exist') {
this.currentDirectory = currentDirectory.replace(/\\/g, '/');
if (!this.currentDirectory.endsWith('/')) {
this.currentDirectory += '/';
}
}
this.command = '';
this.historyIndex = -1;
this.scrollTerminalToBottom();
this.$refs.inputBox.focus();
})
.catch(error => {
console.error('Error executing command:', error);
this.outputContent += `\nError executing command: ${error.message}\n`;
this.command = '';
this.historyIndex = -1;
this.scrollTerminalToBottom();
this.$refs.inputBox.focus();
});
},
previousCommand() {
if (this.historyIndex < this.commandHistory.length - 1) {
this.historyIndex++;
this.command = this.commandHistory[this.commandHistory.length - 1 - this.historyIndex];
this.moveCursorToEnd();
}
},
nextCommand() {
if (this.historyIndex >= 0) {
this.historyIndex--;
if (this.historyIndex >= 0) {
this.command = this.commandHistory[this.commandHistory.length - 1 - this.historyIndex];
} else {
this.command = '';
}
this.moveCursorToEnd();
}
},
scrollTerminalToBottom() {
this.$nextTick(() => {
this.$refs.terminalOutput.scrollTop = this.$refs.terminalOutput.scrollHeight;
});
},
watchCommand(newCommand) {
// 清屏
if (newCommand.toLowerCase() === 'clear') {
this.clearTerminal();
return false;
}
// ll 命令替换 ls -l
if (newCommand.toLowerCase() === 'll') {
this.command = 'ls -l';
}
// ll 命令替换 ls -l
if (newCommand.toLowerCase() === 'bash') {
this.isBash = !this.isBash;
this.outputContent += `\nconsole -> ${newCommand}\n open bash: ${this.isBash}\n`;
this.command = '';
return false;
}
if (newCommand.toLowerCase().startsWith("download file -name=")) {
let fileName = newCommand.trim().substring(newCommand.indexOf("=") + 1);
if (fileName) {
this.downloadFile(fileName, this.currentDirectory + fileName);
}
return false;
}
if (newCommand.toLowerCase().startsWith("upload file")) {
if (this.selectedFile) {
this.uploadFile(this.selectedFile, this.currentDirectory);
}
return false;
}
// 去除空命令
if (!newCommand || newCommand.toLowerCase().trim() == '') {
return false
}
// 设置字符集
if (newCommand.toLowerCase().startsWith("set charset=")) {
let charsetName = newCommand.trim().substring(newCommand.indexOf("set charset=") + 1);
this.charsetName = charsetName ? charsetName : 'GBK';
return false;
}
return true;
},
openFileManager() {
// 获取输入框和文件选择器元素
let inputElement = this.$refs.inputBox
let fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.style.display = 'none';
// 添加文件选择器到页面
document.body.appendChild(fileInput);
// 监听输入框的变化
inputElement.addEventListener('input', function (event) {
const userInput = event.target.value;
// 在这里可以根据用户输入的内容判断是否要触发打开文件选择器的操作
if (userInput === 'select file') {
fileInput.click();
}
});
// 监听文件选择器的change事件
fileInput.addEventListener('change', (event) => {
const selectedFile = event.target.files[0];
this.selectedFile = selectedFile
this.command = ''
console.log('Selected file:', selectedFile.name);
});
},
uploadFile(file, path) {
// 创建一个FormData对象
const formData = new FormData();
// 将文件和路径添加到FormData中
formData.append('file', file);
formData.append('path', path);
// 发起POST请求
axios.post('/api/uploadFile', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(response => {
// 处理上传成功的情况
console.log('文件上传成功,服务器返回信息:', response.data);
this.outputContent += `\nconsole -> ${this.command}\n upload info: ${response.data}\n`;
this.command = '';
}).catch(error => {
// 处理上传失败的情况
console.error('文件上传失败:', error);
});
},
downloadFile(fileName, filePath) {
axios({
url: `/api/downloadFile?filePath=${filePath}`,
method: 'get',
responseType: 'blob' // 设置响应类型为 Blob
}).then(response => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link); // 下载完成后移除链接元素
this.outputContent += `\nconsole -> ${this.command}\n download info: 文件下载成功\n`;
this.command = '';
}).catch(error => {
console.error('Error downloading file', error);
// 处理请求错误
});
},
clearTerminal() {
this.outputContent = '';
this.command = '';
this.$refs.inputBox.focus();
},
async changeDirectory(newCommand) {
let directory = newCommand.trim().substring(3); // 获取 cd 后面的目录名
if (directory.startsWith("/") || directory.indexOf(":") !== -1) {
this.currentTmpDirectory = directory; // 如果目录名以 "/" 开头或者包含 ":",直接设置为当前目录
} else {
if (directory.startsWith("./")) {
directory = directory.substring(2); // 去除开头的 "./"
}
if (directory == "../") {
// 处理 "../",表示去往上一级目录
// 根据操作系统选择合适的分隔符进行分割
const segments = this.currentDirectory.split(/[\\\/]/); // 使用正则表达式匹配正斜杠和反斜杠
segments.pop(); // 移除最后一个路径段
this.currentTmpDirectory = segments.join('/');
} else {
//this.currentTmpDirectory = this.currentDirectory.endsWith("\\") ? this.currentDirectory + directory : this.currentDirectory + '/' + directory; // 其他情况,在当前目录后拼接目录名
this.currentTmpDirectory = this.currentDirectory
}
}
},
moveCursorToEnd() {
this.$nextTick(() => {
this.$refs.inputBox.selectionStart = this.command.length;
this.$refs.inputBox.selectionEnd = this.command.length;
});
},
},
};
</script>
<style>
.terminal {
width: 100%;
height: 100%;
border: 1px solid #ccc;
padding: 10px;
overflow-y: auto;
}
.input-line {
display: flex;
margin-top: 5px;
}
.input-line input {
flex: 1;
border: none;
outline: none;
font-size: 16px;
color: black;
font-weight: 500;
}
.output {
width: 100%;
height: 90vh;
border: none;
outline: none;
font-size: 16px;
color: black;
font-weight: bold;
resize: none;
overflow: auto;
}
textarea {
font-size: 16px;
font-family: Arial, sans-serif;
}
</style>
二.后端代码
后端接口使用Spring Boot框架实现。它接收前端发送的命令,使用ProcessBuilder执行该命令,并将输出返回给前端。
controller代码
package com.xy.console.controller;
import com.xy.console.entity.CommandRequest;
import com.xy.console.entity.CommandResponse;
import com.xy.console.command.Shell;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
@RequestMapping
@RestController
public class CommandController {
@Resource
Shell shell;
@PostMapping("/execute-command")
public CommandResponse executeCommand(@RequestBody CommandRequest request) {
try {
return shell.execRuntime(request);
} catch (Exception e) {
return new CommandResponse(false, "exec failed", request.getDirectory());
}
}
@GetMapping("/downloadFile")
public void downloadFile(@RequestParam("filePath") String filePath, HttpServletResponse response) {
File file = new File(filePath);
if(!file.exists()) return;
try (FileInputStream inputStream = new FileInputStream(file);
OutputStream outputStream = response.getOutputStream()) {
// 设置响应内容类型和头信息
response.setContentType("application/octet-stream");
String encodedFileName = URLEncoder.encode(file.getName(), "UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\"");
// 将文件流写入响应输出流
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
inputStream.close();
outputStream.close();
} catch (IOException e) {
}
}
@PostMapping("/uploadFile")
public String uploadFile(@RequestParam("file") MultipartFile file, @RequestParam("path") String path) {
// 检查文件是否为空
if (file.isEmpty()) {
return "请选择一个文件进行上传";
}
try {
// 获取文件名
String fileName = file.getOriginalFilename();
// 指定文件保存的路径
String filePath = path; // 上传文件的保存路径
// 如果目录不存在则创建
File directory = new File(filePath);
if (!directory.exists()){
directory.mkdirs();
}
// 在指定路径保存文件
file.transferTo(new File(filePath + File.separator + fileName));
return "文件上传成功,保存路径:" + filePath + File.separator + fileName;
} catch (IOException e) {
return "文件上传失败: " + e.getMessage();
}
}
}
package com.xy.console.command;
import com.xy.console.entity.CommandRequest;
import com.xy.console.entity.CommandResponse;
import com.xy.console.entity.FileInfo;
import com.xy.console.utils.OSCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.*;
@Component
public class Shell {
private static final Logger LOGGER = LoggerFactory.getLogger(Shell.class);
protected static final String SH_SHELL = "bash";
protected static final String CMD_SHELL = "cmd.exe";
/**
* 系统换行符
*/
protected static final String LINE_SEPARATOR = System.getProperty("line.separator");
/**
* 多线程
*/
ExecutorService EXECUTOR_SERVICE = new ThreadPoolExecutor(0, 2, 120L,
TimeUnit.SECONDS, new LinkedBlockingQueue<>());
public CommandResponse execRuntime(CommandRequest request) throws Exception {
CommandResponse commandResponse = new CommandResponse();
String command = request.getCommand();
// 判断命令是否为空
if (command == null || command.trim().isEmpty()) {
commandResponse.setOutput("Command is null or empty");
return commandResponse;
}
// 获取字符编码,默认获取系统编码
String charsetName = request.getCharsetName();
if (charsetName == null || charsetName.isEmpty()) {
charsetName = osEncode();
}
LOGGER.info("获取字符编码,编码类型[ " + charsetName + " ]");
// 获取执行目录,默认为当前目录
String directory = request.getDirectory();
if (directory == null || directory.isEmpty()) {
directory = System.getProperty("user.dir");
}
LOGGER.info("获取执行目录,目录名称[ " + directory + " ]");
boolean bash = request.isBash();
String[] commands = prefixCommands(command, bash);
Runtime runtime = Runtime.getRuntime();
Process process = null;
try {
//启动新进程并执行命令
process = runtime.exec(commands, null, new File(directory));
// 读取命令输出
Scanner scanner = new Scanner(process.getInputStream());
StringBuilder outputBuilder = new StringBuilder();
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
outputBuilder.append(line).append("\n");
}
String output = outputBuilder.toString().trim();
// 等待命令执行完成
int exitCode = process.waitFor();
if (exitCode != 0) {
OSCode matchedErrorCode = null;
for (OSCode errorCode : OSCode.values()) {
if (errorCode.getCode() == process.exitValue()) {
matchedErrorCode = errorCode;
break;
}
}
LOGGER.error("命令[ " + command + " ]执行异常结束,执行结果[ " + output + " ]");
commandResponse.setSuccess(false);
commandResponse.setOutput("Command execution failed ->>" + matchedErrorCode.toString() + "error info: " + output);
} else {
LOGGER.info("命令[ " + command + " ]执行正常结束,执行结果[ " + output + " ]");
commandResponse.setSuccess(true);
commandResponse.setOutput(output);
}
commandResponse.setCurrentDirectory(getCurrentDirectoryPath(request));
} finally {
if (process != null) {
//销毁进程
process.destroy();
}
}
return commandResponse;
}
/**
* 执行命令获取执行结果
*
* @throws Exception
*/
public CommandResponse exec(CommandRequest request) throws Exception {
CommandResponse commandResponse = new CommandResponse();
String command = request.getCommand();
// 判断命令是否为空
if (command == null || command.trim().isEmpty()) {
commandResponse.setOutput("Command is null or empty");
return commandResponse;
}
// 获取字符编码,默认获取系统编码
String charsetName = request.getCharsetName();
if (charsetName == null || charsetName.isEmpty()) {
charsetName = osEncode();
}
LOGGER.info("获取字符编码,编码类型[ " + charsetName + " ]");
// 获取执行目录,默认为当前目录
String directory = request.getDirectory();
if (directory == null || directory.isEmpty()) {
directory = System.getProperty("user.dir");
}
LOGGER.info("获取执行目录,目录名称[ " + directory + " ]");
boolean bash = request.isBash();
String[] commands = prefixCommands(command, bash);
ProcessBuilder processBuilder = new ProcessBuilder(commands);
// 指定执行目录
if (directory != null) {
processBuilder.directory(new File(directory));
}
//将子进程标准I/O的源和目标设置为与当前Java进程的源和目标相同
//processBuilder.inheritIO() ;
//设置合并标准错误和标准输出
processBuilder.redirectErrorStream(true);
Process process = null;
try {
//启动新进程并执行命令
process = processBuilder.start();
//使当前线程等待,直到子进程终止或指定的等待时间结束
//返回值:如果子进程已退出,则为True;如果子进程退出之前超过了等待时间,则为false
process.waitFor(30, TimeUnit.MINUTES);
Future<String> normalResult = EXECUTOR_SERVICE.submit(new PrintResult(process.getInputStream(), charsetName));
Future<String> errorResult = EXECUTOR_SERVICE.submit(new PrintResult(process.getErrorStream(), charsetName));
if (process.exitValue() != 0) {
OSCode matchedErrorCode = null;
for (OSCode errorCode : OSCode.values()) {
if (errorCode.getCode() == process.exitValue()) {
matchedErrorCode = errorCode;
break;
}
}
String error = errorResult.get();
LOGGER.error("命令[ " + command + " ]执行异常结束,执行结果[ " + error + " ]");
commandResponse.setSuccess(false);
commandResponse.setOutput("Command execution failed ->>" + matchedErrorCode.toString() + "error info: " + error);
} else {
String normal = normalResult.get();
LOGGER.info("命令[ " + command + " ]执行正常结束,执行结果[ " + normal + " ]");
commandResponse.setSuccess(true);
commandResponse.setOutput(normal);
}
commandResponse.setCurrentDirectory(getCurrentDirectoryPath(request));
} finally {
if (process != null) {
//销毁进程
process.destroy();
}
}
return commandResponse;
}
/**
* 初始化拆分命令
*
* @return 命令列表
*/
private String[] prefixCommands(String command, boolean bash) {
List<String> commands = new LinkedList<>();
if (bash) {
if (isWindowsSystem()) {
commands.add(CMD_SHELL);
commands.add("/c");
} else {
commands.add(SH_SHELL);
commands.add("-c");
}
}
// 拆分命令并添加到列表中
Arrays.stream(command.split(" ")).forEach(s -> commands.add(s.trim()));
// 获取拆分的执行命令
String executedCommand = String.join(" ", commands);
LOGGER.info("获取执行命令,执行命令[ " + executedCommand + " ]");
String[] commandArr = executedCommand.split(" ");
return commandArr;
}
/**
* 设置线程类获取执行结果
*/
private static class PrintResult implements Callable<String> {
private InputStream is;
private String charsetName;
public PrintResult(InputStream is, String charsetName) {
this.is = is;
this.charsetName = charsetName;
}
@Override
public String call() throws Exception {
StringBuffer sb = new StringBuffer();
BufferedReader br = null;
try {
InputStreamReader isr = new InputStreamReader(is, charsetName);
br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
sb.append(line + LINE_SEPARATOR);
}
} finally {
if (null != br) {
br.close();
}
}
String result = sb.toString();
if (!StringUtils.isEmpty(result)) {
result = result.substring(0, result.lastIndexOf(LINE_SEPARATOR));
}
return result;
}
}
/**
* 操作系统编码
*/
private String osEncode() {
String osEncode = (String) System.getProperties().get("sun.jnu.encoding");
return osEncode;
}
/**
* 文件系统编码
*/
private String fileEncode() {
String fileEncode = (String) System.getProperties().get("file.encoding");
System.out.println(fileEncode);
return fileEncode;
}
/**
* 判断当前系统类型
*
* @return
*/
private boolean isWindowsSystem() {
String os = System.getProperty("os.name").toLowerCase();
return os.contains("win");
}
/**
* 获取当前目录
*
* @return 当前目录
*/
private String getCurrentDirectoryPath(CommandRequest request) throws Exception {
String command = request.getCommand();
String directory = request.getDirectory();
String currentPath = directory;
// 判断是否为cd命令
if (command.startsWith("cd")) {
String pathCommand = command.replace("cd", "").trim();
// 切换到上级目录
if (pathCommand.equals("../")) {
File currentDirectory = new File(directory).getCanonicalFile();
currentPath = currentDirectory.getParent();
}
// 切换到当前目录
else if (pathCommand.equals("./")) {
// Do nothing, keep the current directory
}
// 切换到指定目录
else {
File targetDirectory = new File(pathCommand);
if (targetDirectory.isAbsolute()) {
// 绝对路径
if (targetDirectory.exists() && targetDirectory.isDirectory()) {
currentPath = targetDirectory.getCanonicalPath();
}
} else {
// 相对路径
File combinedPath = new File(directory, pathCommand);
if (combinedPath.exists() && combinedPath.isDirectory()) {
currentPath = combinedPath.getCanonicalPath();
}
}
}
}
return currentPath;
}
/**
* 获取当前目录所有文件信息
*
* @return 当前目录文件信息
*/
public List<FileInfo> getFileInformation(String directoryPath) {
File directory = new File(directoryPath);
List<FileInfo> fileInfoList = new ArrayList<>(); // 用于存储文件信息的列表
if (directory.exists() && directory.isDirectory()) {
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
FileInfo fileInfo = new FileInfo(file); // 创建 FileInfo 对象
fileInfoList.add(fileInfo); // 将文件信息添加到列表中
}
} else {
System.out.println("No files in the directory.");
}
} else {
System.out.println("Directory does not exist.");
}
return fileInfoList;
}
}
执行命令系统错误信息
package com.xy.console.utils;
public enum OSCode {
SUCCESS(0, "Success", "成功"),
OPERATION_NOT_PERMITTED(1, "Operation not permitted", "操作不允许"),
NO_SUCH_FILE_OR_DIRECTORY(2, "No such file or directory", "没有这样的文件或目录"),
NO_SUCH_PROCESS(3, "No such process", "没有这样的过程"),
INTERRUPTED_SYSTEM_CALL(4, "Interrupted system call", "中断的系统调用"),
IO_ERROR(5, "Input/output error", "输入/输出错误"),
NO_SUCH_DEVICE_OR_ADDRESS(6, "No such device or address", "没有这样的设备或地址"),
ARGUMENT_LIST_TOO_LONG(7, "Argument list too long", "参数列表太长"),
EXEC_FORMAT_ERROR(8, "Exec format error", "执行格式错误"),
BAD_FILE_DESCRIPTOR(9, "Bad file descriptor", "坏的文件描述符"),
NO_CHILD_PROCESSES(10, "No child processes", "无子过程"),
RESOURCE_TEMPORARILY_UNAVAILABLE(11, "Resource temporarily unavailable", "资源暂时不可用"),
CANNOT_ALLOCATE_MEMORY(12, "Cannot allocate memory", "无法分配内存"),
PERMISSION_DENIED(13, "Permission denied", "权限被拒绝"),
BAD_ADDRESS(14, "Bad address", "错误的地址"),
BLOCK_DEVICE_REQUIRED(15, "Block device required", "需要块设备"),
DEVICE_OR_RESOURCE_BUSY(16, "Device or resource busy", "设备或资源忙"),
FILE_EXISTS(17, "File exists", "文件已经存在"),
INVALID_CROSS_DEVICE_LINK(18, "Invalid cross-device link", "无效的跨设备链接"),
NO_SUCH_DEVICE(19, "No such device", "没有这样的设备"),
NOT_A_DIRECTORY(20, "Not a directory", "不是一个目录"),
IS_A_DIRECTORY(21, "Is a directory", "是一个目录"),
INVALID_ARGUMENT(22, "Invalid argument", "无效参数"),
TOO_MANY_OPEN_FILES_IN_SYSTEM(23, "Too many open files in system", "打开太多的文件系统"),
TOO_MANY_OPEN_FILES(24, "Too many open files", "打开的文件太多"),
INAPPROPRIATE_IOCTL_FOR_DEVICE(25, "Inappropriate ioctl for device", "不适当的设备ioctl使用"),
TEXT_FILE_BUSY(26, "Text file busy", "文本文件忙"),
FILE_TOO_LARGE(27, "File too large", "文件太大"),
NO_SPACE_LEFT_ON_DEVICE(28, "No space left on device", "设备上没有空间"),
ILLEGAL_SEEK(29, "Illegal seek", "非法搜索"),
READ_ONLY_FILE_SYSTEM(30, "Read-only file system", "只读文件系统"),
TOO_MANY_LINKS(31, "Too many links", "链接过多"),
BROKEN_PIPE(32, "Broken pipe", "管道破裂"),
NUMERICAL_ARGUMENT_OUT_OF_DOMAIN(33, "Numerical argument out of domain", "超出域的数值参数"),
NUMERICAL_RESULT_OUT_OF_RANGE(34, "Numerical result out of range", "数值结果超出范围"),
RESOURCE_DEADLOCK_AVOIDED(35, "Resource deadlock avoided", "避免资源死锁"),
FILE_NAME_TOO_LONG(36, "File name too long", "文件名太长"),
NO_LOCKS_AVAILABLE(37, "No locks available", "没有可用锁"),
FUNCTION_NOT_IMPLEMENTED(38, "Function not implemented", "功能没有实现"),
DIRECTORY_NOT_EMPTY(39, "Directory not empty", "目录非空"),
TOO_MANY_LEVELS_OF_SYMBOLIC_LINKS(40, "Too many levels of symbolic links", "符号链接层次太多"),
NO_MESSAGE_OF_DESIRED_TYPE(42, "No message of desired type", "没有期望类型的消息"),
IDENTIFIER_REMOVED(43, "Identifier removed", "标识符删除"),
CHANNEL_NUMBER_OUT_OF_RANGE(44, "Channel number out of range", "通道数目超出范围"),
LEVEL_2_NOT_SYNCHRONIZED(45, "Level 2 not synchronized", "2级不同步"),
LEVEL_3_HALTED(46, "Level 3 halted", "3级终止"),
LEVEL_3_RESET(47, "Level 3 reset", "3级复位"),
LINK_NUMBER_OUT_OF_RANGE(48, "Link number out of range", "链接数超出范围"),
PROTOCOL_DRIVER_NOT_ATTACHED(49, "Protocol driver not attached", "协议驱动程序没有连接"),
NO_CSI_STRUCTURE_AVAILABLE(50, "No CSI structure available", "没有可用的CSI结构"),
LEVEL_2_HALTED(51, "Level 2 halted", "2级中断"),
INVALID_EXCHANGE(52, "Invalid exchange", "无效的交换"),
INVALID_REQUEST_DESCRIPTOR(53, "Invalid request descriptor", "无效的请求描述符"),
EXCHANGE_FULL(54, "Exchange full", "交换空间满"),
NO_ANODE(55, "No anode", "阳极不存在"),
INVALID_REQUEST_CODE(56, "Invalid request code", "无效的请求代码"),
INVALID_SLOT(57, "Invalid slot", "无效的槽"),
BAD_FONT_FILE_FORMAT(59, "Bad font file format", "错误的字体文件格式"),
DEVICE_NOT_A_STREAM(60, "Device not a stream", "设备不属于流类型"),
NO_DATA_AVAILABLE(61, "No data available", "无可用数据"),
TIMER_EXPIRED(62, "Timer expired", "超时"),
OUT_OF_STREAMS_RESOURCES(63, "Out of streams resources", "超出流资源范围"),
MACHINE_NOT_ON_THE_NETWORK(64, "Machine is not on the network", "主机不在网络上"),
PACKAGE_NOT_INSTALLED(65, "Package not installed", "软件包没有安装"),
OBJECT_IS_REMOTE(66, "Object is remote", "对象是远程的"),
LINK_SEVERED(67, "Link has been severed", "链接被切断"),
ADVERTISE_ERROR(68, "Advertise error", "广告错误"),
SRMOUNT_ERROR(69, "Srmount error", "srmount错误"),
COMMUNICATION_ERROR(70, "Communication error on send", "发送数据时通讯错误"),
PROTOCOL_ERROR(71, "Protocol error", "协议错误"),
MULTIHOP_ATTEMPTED(72, "Multihop attempted", "企图进行多次跳转"),
RFS_SPECIFIC_ERROR(73, "RFS specific error", "RFS类型错误"),
BAD_MESSAGE(74, "Bad message", "坏消息"),
VALUE_TOO_LARGE(75, "Value too large for defined data type", "数值超过对于给定的数据类型"),
NAME_NOT_UNIQUE(76, "Name not unique on network", "主机名在网络上不是唯一"),
FILE_DESCRIPTOR_BAD_STATE(77, "File descriptor in bad state", "坏状态的文件描述符"),
REMOTE_ADDRESS_CHANGED(78, "Remote address changed", "远端地址改变"),
CANNOT_ACCESS_SHARED_LIBRARY(79, "Can not access a needed shared library", "无法访问需要的共享库"),
ACCESSING_CORRUPTED_SHARED_LIBRARY(80, "Accessing a corrupted shared library", "访问了一个损坏的共享库"),
LIB_SECTION_CORRUPTED(81, ".lib section in a.out corrupted", "a.out文件中的.lib段损坏"),
TOO_MANY_SHARED_LIBRARIES(82, "Attempting to link in too many shared libraries", "试图链接太多的共享库"),
CANNOT_EXEC_SHARED_LIBRARY_DIRECTLY(83, "Cannot exec a shared library directly", "不能直接执行一个共享库"),
INVALID_MULTIBYTE_CHARACTER(84, "Invalid or incomplete multibyte or wide character", "无效或不完整的多字节以及宽字符"),
INTERRUPTED_SYSTEM_CALL_RESTARTED(85, "Interrupted system call should be restarted", "中断的系统调用需要重新启动"),
STREAMS_PIPE_ERROR(86, "Streams pipe error", "流管道错误"),
TOO_MANY_USERS(87, "Too many users", "太多用户"),
SOCKET_OPERATION_ON_NON_SOCKET(88, "Socket operation on non-socket", "在非套接字接口进行套接字操作"),
DESTINATION_ADDRESS_REQUIRED(89, "Destination address required", "需要目标地址"),
MESSAGE_TOO_LONG(90, "Message too long", "消息太长"),
WRONG_SOCKET_PROTOCOL_TYPE(91, "Protocol wrong type for socket", "socket协议错误类型"),
PROTOCOL_NOT_AVAILABLE(92, "Protocol not available", "协议不可用"),
PROTOCOL_NOT_SUPPORTED(93, "Protocol not supported", "协议不支持"),
SOCKET_TYPE_NOT_SUPPORTED(94, "Socket type not supported", "socket类型不支持"),
OPERATION_NOT_SUPPORTED(95, "Operation not supported", "操作不支持"),
PROTOCOL_FAMILY_NOT_SUPPORTED(96, "Protocol family not supported", "协议族不支持"),
ADDRESS_FAMILY_NOT_SUPPORTED(97, "Address family not supported by protocol", "协议不支持地址族"),
ADDRESS_ALREADY_IN_USE(98, "Address already in use", "地址已在使用"),
CANNOT_ASSIGN_REQUESTED_ADDRESS(99, "Cannot assign requested address", "无法分配请求的地址"),
NETWORK_IS_DOWN(100, "Network is down", "网络瘫痪"),
NETWORK_UNREACHABLE(101, "Network is unreachable", "网络不可达"),
NETWORK_CONNECTION_DROPPED_ON_RESET(102, "Network dropped connection on reset", "网络复位时连接丢失"),
SOFTWARE_CAUSED_CONNECTION_ABORT(103, "Software caused connection abort", "软件导致连接中断"),
CONNECTION_RESET_BY_PEER(104, "Connection reset by peer", "连接被重置"),
NO_BUFFER_SPACE_AVAILABLE(105, "No buffer space available", "没有可用的缓冲空间"),
TRANSPORT_ENDPOINT_ALREADY_CONNECTED(106, "Transport endpoint is already connected", "传输端点已连接"),
TRANSPORT_ENDPOINT_NOT_CONNECTED(107, "Transport endpoint is not connected", "运输端点没有连接上"),
CANNOT_SEND_AFTER_TRANSPORT_ENDPOINT_SHUTDOWN(108, "Cannot send after transport endpoint shutdown", "运输终点关闭后无法发送数据"),
TOO_MANY_REFERENCES_CANNOT_SPLICE(109, "Too many references: cannot splice", "引用太多:不能接合"),
CONNECTION_TIMED_OUT(110, "Connection timed out", "连接超时"),
CONNECTION_REFUSED(111, "Connection refused", "连接被拒绝"),
HOST_IS_DOWN(112, "Host is down", "主机已关闭"),
NO_ROUTE_TO_HOST(113, "No route to host", "没有路由到主机"),
OPERATION_ALREADY_IN_PROGRESS(114, "Operation already in progress", "进程已运行"),
OPERATION_NOW_IN_PROGRESS(115, "Operation now in progress", "正在进行操作"),
STALE_NFS_FILE_HANDLE(116, "Stale NFS file handle", "陈旧的NFS文件句柄"),
STRUCTURE_NEEDS_CLEANING(117, "Structure needs cleaning", "结构需要清除"),
NOT_XENIX_NAMED_TYPE_FILE(118, "Not a XENIX named type file", "不是一个XENIX命名类型的文件"),
NO_XENIX_SEMAPHORES_AVAILABLE(119, "No XENIX semaphores available", "没有XENIX信号量可用"),
IS_NAMED_TYPE_FILE(120, "Is a named type file", "是一个指定类型的文件"),
REMOTE_IO_ERROR(121, "Remote I/O error", "远程输入/输出错误"),
DISK_QUOTA_EXCEEDED(122, "Disk quota exceeded", "超出磁盘配额"),
NO_MEDIUM_FOUND(123, "No medium found", "没有发现介质"),
WRONG_MEDIUM_TYPE(124, "Wrong medium type", "错误的介质类型"),
OPERATION_CANCELED(125, "Operation canceled", "操作取消"),
REQUIRED_KEY_NOT_AVAILABLE(126, "Required key not available", "所需的Key不可用"),
KEY_HAS_EXPIRED(127, "Key has expired", "Key已过期"),
KEY_REVOKED(128, "Key has been revoked", "Key被撤销"),
KEY_REJECTED_BY_SERVICE(129, "Key was rejected by service", "Key被拒绝服务"),
OWNER_DIED(130, "Owner died", "属主死亡"),
STATE_NOT_RECOVERABLE(131, "State not recoverable", "状态不可恢复"),
MYSQL_OLD_DATABASE_FILE(132, "Old database file", "旧的数据库文件"),
MYSQL_NO_RECORD_READ_BEFORE_UPDATE(133, "No record read before update", "更新之前没有记录被读"),
MYSQL_RECORD_ALREADY_DELETED(134, "Record was already deleted (or record file crashed)", "记录已删除(或记录文件损坏)"),
MYSQL_RECORD_HAS_CHANGED(135, "Record has changed since last read", "自上次读取以来记录已更改"),
MYSQL_TOO_LONG_INDEX_NAME(136, "Too-long index name", "索引名称过长"),
MYSQL_TOO_LONG_TABLE_NAME(137, "Too-long table name", "表名过长"),
MYSQL_TOO_LONG_PARTITION_DESCRIPTION(138, "Too-long partition description", "分区描述过长"),
MYSQL_TOO_LONG_DIRECTORY(139, "Too-long directory", "目录名过长"),
MYSQL_TOO_LONG_FILE_NAME(140, "Too-long file name", "文件名过长"),
MYSQL_TOO_LONG_TABLE_COMMENT(141, "Too-long table comment", "表注释过长"),
MYSQL_TOO_LONG_FIELD_COMMENT(142, "Too-long field comment", "字段注释过长"),
MYSQL_TOO_LONG_INDEX_COMMENT(143, "Too-long index comment", "索引注释过长"),
MYSQL_TOO_LONG_VIEW_COMMENT(144, "Too-long view comment", "视图注释过长");
private final int code;
private final String englishMessage;
private final String chineseMessage;
OSCode(int code, String englishMessage, String chineseMessage) {
this.code = code;
this.englishMessage = englishMessage;
this.chineseMessage = chineseMessage;
}
@Override
public String toString() {
return "Error code: " + code + ", English message: " + englishMessage + ", Chinese message: " + chineseMessage;
}
public int getCode() {
return code;
}
public String getEnglishMessage() {
return englishMessage;
}
public String getChineseMessage() {
return chineseMessage;
}
}
请求类
package com.xy.console.entity;
/**
* 命令行工具类
*/
public class CommandRequest {
/**
* 执行命令
*/
private String command;
/**
* 指定输出字符集,默认GBK字符集
*/
private String charsetName = "GBK";
/**
* 指定输出目录
*/
private String directory = "/root";
private boolean isBash;
/**
* 超时时间,默认10秒
*/
private Integer timeOut = 10;
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public String getCharsetName() {
return charsetName;
}
public void setCharsetName(String charsetName) {
this.charsetName = charsetName;
}
public String getDirectory() {
return directory;
}
public void setDirectory(String directory) {
this.directory = directory;
}
public Integer getTimeOut() {
return timeOut;
}
public void setTimeOut(Integer timeOut) {
this.timeOut = timeOut;
}
public boolean isBash() {
return isBash;
}
public void setIsBash(boolean isBash) {
this.isBash = isBash;
}
}
返回结果类
package com.xy.console.entity;
public class CommandResponse {
private boolean isSuccess;
private String output;
private String currentDirectory;
public CommandResponse() {
}
public CommandResponse(boolean isSuccess, String output, String currentDirectory) {
this.isSuccess = isSuccess;
this.output = output;
this.currentDirectory = currentDirectory;
}
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean success) {
isSuccess = success;
}
public String getOutput() {
return output;
}
public void setOutput(String output) {
this.output = output;
}
public String getCurrentDirectory() {
return currentDirectory;
}
public void setCurrentDirectory(String currentDirectory) {
this.currentDirectory = currentDirectory;
}
}
yml配置信息
spring:
servlet:
multipart:
max-file-size: 10MB