背景:老大做了一个 SDK,包含字符加解密、文件加解密,要求能从前端访问,并且能演示的 Demo。
思路:html 写页面,js 发送请求,freemarker 做简单的参数交互,JAVA 后端处理。
一、项目依赖
● java17
● springboot 3.1.2
● 模板技术:spring-boot-starter-freemarker 2.3.32
● 工具包:commons-io 2.16.0
● pom 文件:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.xxx.xx.x</groupId>
<artifactId>xx-x</artifactId>
<version>3.0-SNAPSHOT</version>
</parent>
<artifactId>x-sdk-service</artifactId>
<modelVersion>4.0.0</modelVersion>
<description>sdk 项目服务</description>
<packaging>jar</packaging>
<name>sdk-service</name>
<properties>
<jdk.version>17</jdk.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>3.1.2</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
<!-- SpringBoot 拦截器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Spring框架基本的核心工具 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.8</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.30</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 前端渲染视图技术 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.16.1</version>
</dependency>
</dependencies>
</project>
二、前端代码
使用的模板技术,JS + Freemark。“${baseUrl}”指向的是后端配置的IP地址,可改为静态的,但是那样html页面就写死了。文件放在项目:resource/templates下,无则需要创建。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>演示页</title>
</head>
<script>
// 访问地址
const baseUrl = "${baseUrl}"
/**
* 加密
*/
function getEnc() {
// 处理传入数据
let data = document.getElementById("encText").value;
data = btoa(data);
// 组装请求
const oReq = new XMLHttpRequest();
const url = baseUrl + "/enc?data=" + data;
console.log("request " + url)
oReq.open("GET", url, true);
// 响应类型
oReq.responseType = "text";
oReq.onprogress = function (result) {
console.log(result)
// 失败处理
if (fail(result)) {
return;
}
// 处理响应
const data = result.currentTarget.response;
let parse = JSON.parse(data);
const text = parse.data;
document.getElementById("decText").value = String(text);
};
oReq.send();
}
/**
* 解密
*/
function getDec() {
let data = document.getElementById("decText").value;
const oReq = new XMLHttpRequest();
data = btoa(data);
const url = baseUrl + "/dec?data=" + data;
console.log("request " + url)
oReq.open("GET", url, true);
// 响应类型
oReq.responseType = "text";
oReq.onprogress = function (result) {
console.log(result)
if (fail(result)) {
return;
}
const data = result.currentTarget.response;
let parse = JSON.parse(data);
const text = parse.data;
document.getElementById("encText").value = String(atob(text));
};
oReq.send();
}
/**
* 文件加密
*/
function getEncFile() {
const fileInput = document.getElementById('encFile');
const file = fileInput.files[0];
let formData = new FormData();
formData.append("file", file)
const oReq = new XMLHttpRequest();
const url = baseUrl + "/encFile";
console.log("request " + url)
oReq.open("POST", url, true);
// 响应类型
oReq.onprogress = function (result) {
console.log(result)
if (fail(result)) {
return;
}
const data = result.currentTarget.response;
let parse = JSON.parse(data);
const fileName = parse.data;
// 下载文件
downloadFile(baseUrl + "/downLoadFile/" + fileName, fileName)
// 原组件置空
fileInput.value = '';
};
oReq.send(formData);
}
/**
* 文件解密
*/
function getDecFile() {
const fileInput = document.getElementById('decFile');
const file = fileInput.files[0];
let formData = new FormData();
formData.append("file", file)
const oReq = new XMLHttpRequest();
const url = baseUrl + "/decFile";
console.log("request " + url)
oReq.open("POST", url, true);
// 响应类型
oReq.onload = function (result) {
console.log(result)
if (fail(result)) {
console.log("encFile:" + result)
return;
}
const data = result.currentTarget.response;
let parse = JSON.parse(data);
const fileName = parse.data;
downloadFile(baseUrl + "/downLoadFile/" + fileName, fileName)
fileInput.value = '';
};
oReq.send(formData);
}
/**
* 下载文件
* @param url
* @param fileName
*/
function downloadFile(url, fileName) {
console.log("downloadFile:" + url)
console.log("downloadFile fileName:" + fileName)
fetch(url)
.then(response => response.blob())
.then(blob => {
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = fileName;
link.target = "_blank"; // 可选,如果希望在新窗口中下载文件,请取消注释此行
link.click();
});
}
/**
* 请求是否失败
* @param result 请求
* @returns {boolean} true 失败
*/
function fail(result) {
const data = result.currentTarget.response;
let parse = JSON.parse(data);
if (200 !== parse.code) {
alert("请求失败:" + parse.msg)
return true;
}
return false;
}
</script>
<body style="text-align: center;margin-top: auto">
<h1>加密工具演示页</h1>
<!-- 文本加密 -->
<div style="margin-top: 48px">
<h3>文本加密</h3>
<div>
<input id="encText" type="text" style="width: 200px;"/>
<button onclick="getEnc();" style="margin-left: 12px">加密</button>
</div>
<div style="margin-top: 12px">
<input id="decText" type="text" style="width: 200px;"/>
<button onclick="getDec();" style="margin-left: 12px">解密</button>
</div>
</div>
<!-- 文件加密 -->
<div style="margin-top: 48px;">
<h3>文件加解密</h3>
<div style="margin-top: 24px">
文件加密:
<input id="encFile" type="file" onchange="getEncFile()"/>
</div>
<div style="margin-top: 24px;">
文件解密:
<input id="decFile" type="file" onchange="getDecFile()"/>
</div>
</div>
</body>
</html>
三、后端配置
application配置文件
server:
port: 12321
tmv: