JS+Springboot做一个交互Demo

背景:老大做了一个 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:
  baseUrl: http://localhost:12321
  baseDir: ./config/


# application.yml
spring:
  servlet:
    multipart:
      max-file-size: 10000MB
      max-request-size: 10000MB
  #配置freemarker
  freemarker:
    #指定HttpServletRequest的属性是否可以覆盖controller的model的同名项
    allow-request-override: false
    #req访问request
    request-context-attribute: req
    #后缀名freemarker默认后缀为.ftl,当然你也可以改成自己习惯的.html
    suffix: .ftl
    #设置响应的内容类型
    content-type: text/html;charset=utf-8
    #是否允许mvc使用freemarker
    enabled: true
    #是否开启template caching
    cache: false
    #设定模板的加载路径,多个以逗号分隔,默认: [“classpath:/templates/”]
    template-loader-path: classpath:/templates/
    #设定Template的编码
    charset: UTF-8
    # 设置静态文件路径,js,css等
  mvc:
    static-path-pattern: /static/**

四、后端代码

@RestController
@RequestMapping
@Slf4j
public class EncryptController {

    @Value("${tmv.baseUrl:http://localhost:12321}")
    String baseUrl;
    @Value("${tmv.baseDir:D:\\temp\\temp\\}")
    String baseDir;

    private static KmService kmService;

    /**
     * 初始化 SDK
     */
    @PostConstruct
    public void init() {
        try {
            log.info("load kmService start, baseDir: {}", baseDir);
            kmService = KmService.getInstance(baseDir);
            log.info("load kmService success");
        } catch (Exception e) {
            log.error("SDK加载失败,请检查");
            log.error("load kmService error", e);
        }
    }

    @RequestMapping("/")
    public ModelAndView index() {
        // 跳转到index.ftl 模板
        ModelAndView mv = new ModelAndView("index");
        // 添加属性
        mv.addObject("baseUrl", baseUrl);
        return mv;
    }

    @GetMapping("enc")
    public TResult enc(@RequestParam String data) {
        log.info("enc request: {}", data);
        String result;
        try {
            result = kmService.enc(data, null);
        } catch (Exception e) {
            log.error("enc error", e);
            result = e.getMessage();
        }
        log.info("enc resp: {}", result);
        return TResult.success(result);
    }

    @GetMapping("dec")
    public TResult dec(@RequestParam String data) {
        log.info("dec : {}", data);
        // 需要转码
        byte[] bytes = Base64.decodeBase64(data);
        log.info("dec : {}", data);
        String result;
        try {
            result = kmService.dec(new String(bytes), null);
        } catch (Exception e) {
            log.error("dec error", e);
            result = e.getMessage();
        }
        log.info("dec resp: {}", result);
        return TResult.success(result);
    }

    @PostMapping("encFile")
    public TResult encFile(@RequestParam MultipartFile file) {
        log.info("fileName request: {}", file.getOriginalFilename());
        long size = file.getSize();
        log.info("file size: {} bit {} kb", size, size / 1000);
        String result;
        try {
            // 创建临时文件
            File tempFile = File.createTempFile(System.currentTimeMillis() + "", "");
            byte[] bytes = file.getBytes();
            FileUtils.copyInputStreamToFile(new ByteArrayInputStream(bytes), tempFile);
            File destFile = new File(baseDir + UUIDGenerator.getUUID() + ".enc.txt", "");
            // 加密
            boolean flag = kmService.fileEncrypt(tempFile.getAbsolutePath(), destFile.getAbsolutePath());
            if (flag) {
                result = destFile.getName();
            } else {
                throw new RuntimeException("处理失败,请检查输入信息是否有误");
            }
        } catch (Exception e) {
            log.error("encFile error", e);
            return TResult.fail(e.getMessage());
        }
        log.info("enc resp: {}", result);
        return TResult.success(result);
    }

    @PostMapping("decFile")
    public TResult decFile(@RequestParam MultipartFile file) {
        log.info("decFile fileName request: {}", file.getOriginalFilename());
        long size = file.getSize();
        log.info("file size: {} bit {} kb", size, size / 1000);
        String result;
        try {
            // 创建临时文件
            File tempFile = File.createTempFile(System.currentTimeMillis() + "", "");
            byte[] bytes = file.getBytes();
            FileUtils.copyInputStreamToFile(new ByteArrayInputStream(bytes), tempFile);
            File destFile = new File(baseDir + UUIDGenerator.getUUID() + ".dec.txt", "");
            // 加密
            boolean flag = kmService.fileDecrypt(tempFile.getAbsolutePath(), destFile.getAbsolutePath());
            if (flag) {
                result = destFile.getName();
            } else {
                throw new RuntimeException("处理失败,请检查输入信息是否有误");
            }
        } catch (Exception e) {
            log.error("decFile error", e);
            return TResult.fail(e.getMessage());
        }
        log.info("enc resp: {}", result);
        return TResult.success(result);
    }

    @GetMapping("downLoadFile/{fileName}")
    public void downLoadFile(HttpServletResponse response, @PathVariable("fileName") String fileName) throws IOException {
        log.info("downLoadFile fileName request: {}", fileName);
        String filePath = baseDir + fileName;
        log.info("filePath :{}", filePath);
        File file = new File(filePath);
        try {
            copy2respond(response, file);
        } catch (Exception e) {
            log.error("decFile error", e);
        }
    }

    /**
     * 文件转下载流
     *
     * @param response 响应
     * @param file     文件
     * @throws IOException 异常
     */
    private static void copy2respond(HttpServletResponse response, File file) throws IOException {
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=" + file.getName());

        FileInputStream fileInputStream = new FileInputStream(file);
        OutputStream out = response.getOutputStream();

        byte[] buffer = new byte[4096];
        int length;
        while ((length = fileInputStream.read(buffer)) > 0) {
            out.write(buffer, 0, length);
        }
        out.flush();
        fileInputStream.close();
    }

}

响应对象

@Data
public class TResult implements Serializable {

    private static final long serialVersionUID = 1L;

    @Schema(description = "响应码")
    @Getter
    private int code;

    @Schema(description = "响应消息")
    @Getter
    private String msg;

    @Schema(description = "响应数据")
    @Getter
    private Object data;

    public static TResult success(Object data){
        TResult tResult = new TResult();
        tResult.setCode(200);
        tResult.setMsg("OK");
        tResult.setData(data);
        return tResult;
    }

    public static TResult fail(String messag){
        TResult tResult = new TResult();
        tResult.setCode(500);
        tResult.setMsg(messag);
        tResult.setData(null);
        return tResult;
    }
}

五、页面效果

在这里插入图片描述

最后,作为一个Javaer,会点前端是OK的。但,技不外露,最好不要随便接前端需求。还好最终老大放了一条生路,否则笔者大概率要学习深入理解Html + css + JavaScript了。
(因为笔者觉得只是两个输入框所以自告奋勇的接下来,谁知道后面又要加远程访问、文件加解密、UI优化、全局异常捕获、人性化提示…差点翻车)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/901187.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CSS 样式 box-sizing: border-box; 用于控制元素的盒模型如何计算宽度和高度

文章目录 box-sizing: border-box; 的含义默认盒模型 (content-box)border-box 盒模型 在微信小程序中的应用示例 在微信小程序中&#xff0c;CSS 样式 box-sizing: border-box; 用于控制元素的盒模型如何计算宽度和高度。具体来说&#xff0c; box-sizing: border-box; 会改…

【已解决】C# NPOI如何在Excel文本中增加下拉框

前言 上图&#xff01; 解决方法 直接上代码&#xff01;&#xff01;&#xff01;&#xff01;综合了各个大佬的自己修改了一下&#xff01;可以直接规定在任意单元格进行设置。 核心代码方法块 #region Excel增加下拉框/// <summary>/// 增加下拉框选项/// </s…

Python游戏开发超详细(基础理论知识篇)

一、引导&#xff1a; Python游戏开发是一个非常有趣且富有挑战性的领域。通过Python&#xff0c;你可以利用其强大的库和框架来创建各种类型的游戏&#xff0c;从简单的2D游戏到复杂的3D游戏。以下是第一课的基础理论知识&#xff0c;帮助你入门Python游戏开发。 二、理论知识…

中小企业设备资源优化:Spring Boot系统实现

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

部署seatunnel2.3.8

部署seatunnel web参考&#xff1a;SeaTunnel Web1.0.0安装_plugindiscoveryutil.getallconnectors-CSDN博客 配置&#xff1a;两台centos服务器&#xff0c;2master2worker 一、下载包 v2.3.8[bin] apache-seatunnel-2.3.8-bin.tar.gz 将包上传到master节点和worker节点所…

Python开发日记 -- 实现bin文件的签名

目录 1.数据的不同表现形式签名值不一样&#xff1f; 2.Binascii模块简介 3.问题定位 4.问题总结 1.数据的不同表现形式签名值不一样&#xff1f; Happy Muscle试运行了一段时间&#xff0c;组内同事再一次提出了新的需求&#xff1a;需要对bin文件签名。 PS&#xff1a;服…

使用代码编辑组件的npm包

使用代码编辑组件的npm包 文章说明核心代码运行截图源码下载 文章说明 我将书写的代码编辑组件打包为npm包&#xff0c;下载即可使用&#xff0c;目前是1.0.4版本&#xff0c;虽然功能还有一些bug&#xff0c;但是可以较为简单的使用 npm地址 核心代码 安装依赖 npm i bingbing…

H7-TOOL的LUA小程序教程第16期:脉冲测量,4路PWM,多路GPIO和波形打印(2024-10-25, 更新完毕)

LUA脚本的好处是用户可以根据自己注册的一批API&#xff08;当前TOOL已经提供了几百个函数供大家使用&#xff09;&#xff0c;实现各种小程序&#xff0c;不再限制Flash里面已经下载的程序&#xff0c;就跟手机安装APP差不多&#xff0c;所以在H7-TOOL里面被广泛使用&#xff…

OpenCV-物体跟踪

文章目录 一、物体跟踪的定义二、OpenCV中的物体跟踪算法三、OpenCV物体跟踪的实现步骤四、代码实现五、注意事项 OpenCV是一个开源的计算机视觉和机器学习软件库&#xff0c;它提供了丰富的功能来实现物体跟踪。以下是对OpenCV中物体跟踪的详细解释&#xff1a; 一、物体跟踪的…

清华大学《2022年+2021年822自动控制原理真题》 (完整版)

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;《清华大学822自控考研资料》的真题篇。后续会持续更新更多学校&#xff0c;更多年份的真题&#xff0c;记得关注哦~ 目录 2022年真题 2021年真题 Part1&#xff1a;2022年2021年完整版真题 2022年真题 2021年真题…

论文笔记:通用世界模型WorldDreamer

整理了WorldDreamer: Towards General World Models for Video Generation via Predicting Masked Tokens 论文的阅读笔记 背景模型实验 背景 现有的世界模型仅限于游戏或驾驶等特定场景&#xff0c;限制了它们捕捉一般世界动态环境复杂性的能力。针对这一挑战&#xff0c;本文…

【若依笔记】-- 精简若依项目只保留系统管理

环境&#xff1a;最近项目需要计划使用若依来开发软件&#xff0c;使用若依有一个问题&#xff0c;若依代码框架还是比较冗余&#xff0c;不够精简&#xff0c;还有一点是若依Security权限校验&#xff0c;对于实现一对多的前台&#xff0c;比较麻烦&#xff0c;我这边的业务是…

大一物联网要不要转专业,转不了该怎么办?

有幸在2014年&#xff0c;踩中了物联网的风口&#xff0c;坏消息&#xff0c;牛马的我&#xff0c;一口汤都没喝上。 依稀记得&#xff0c;当时市场部老大&#xff0c;带我去上海参加电子展会&#xff0c;印象最深的&#xff0c;一些物联网云平台&#xff0c;靠着一份精美PPT&a…

【Python爬虫系列】_031.Scrapy_模拟登陆中间件

课 程 推 荐我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)教程合集 👈👈…

接口测试(八)jmeter——参数化(CSV Data Set Config)

一、CSV Data Set Config 需求&#xff1a;批量注册5个用户&#xff0c;从CSV文件导入用户数据 1. 【线程组】–>【添加】–>【配置元件】–>【CSV Data Set Config】 2. 【CSV数据文件设置】设置如下 3. 设置线程数为5 4. 运行后查看响应结果

Linux 进程概念

目录 冯诺依曼体系结构&#xff08;了解&#xff09; 周边知识 操作系统 如何管理 解释打印 ★库函数 ★系统调用 进程 概念 PCB 结构示意图 系统调用 监控脚本 gitpid / gitppid 解释样例 chdir /proc 解释样例 运行起来后删除磁盘中小体积的可执行程序 …

RHCSA第二次作业

4、将整个 /etc 目录下的文件全部打包并用 gzip 压缩成/back/etcback.tar.gz 5、使当前用户永久生效的命令别名&#xff1a;写一个命令命为hello,实现的功能为每输入一次hello命令&#xff0c;就有hello&#xff0c;everyone写入文件/file.txt中。 6、创建mygroup组群&#xff…

IDEA关联Tomcat——最新版本IDEA 2024

1.链接Tomcat到IDEA上 添加Tomcat到IDEA上有两种方式&#xff1a; 第一种&#xff1a; &#xff08;1&#xff09;首先&#xff0c;来到欢迎界面&#xff0c;找到左侧的Customize选项 &#xff08;2&#xff09;然后找到Build、Execution、Deployment选项 &#xff08;3&am…

ruoyi域名跳转缓存冲突问题(解决办法修改:session名修改session的JSESSIONID名称)

【版权所有&#xff0c;文章允许转载&#xff0c;但须以链接方式注明源地址&#xff0c;否则追究法律责任】【创作不易&#xff0c;点个赞就是对我最大的支持】 前言 仅作为学习笔记&#xff0c;供大家参考 总结的不错的话&#xff0c;记得点赞收藏关注哦&#xff01; 目录 前…

Java 输入与输出(I/O)之字节流、字符流与转换流

Java 输入/输出流&#xff08;Input/Output&#xff0c;简称I/O&#xff09;是Java语言用于读写数据的API&#xff0c;它提供了一系列类和接口&#xff0c;用于读取和写入各种类型的数据信息。 I/O流按数据单元&#xff08;粒度大小&#xff09;可分为字节流&#xff08;以字节…