JS+Springboot做一个交互Demo

news2024/11/23 15:18:18

背景:老大做了一个 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优化、全局异常捕获、人性化提示…差点翻车)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

element 按钮变形 el-button样式异常

什么都没动&#xff0c;element UI的按钮变形了&#xff0c;莫名其妙&#xff0c;连官网的也变形了&#xff0c;换了其它浏览器又正常&#xff0c; 难道这是element UI的问题&#xff1f;NO&#xff0c;是浏览器的插件影响到了&#xff01;去扩展插件里面一个个关闭扩展&#x…

使用高德API和MapboxGL实现路径规划并语音播报

概述 本文使用高德API实现位置查询和路径规划&#xff0c;使用MapboxGL完成地图交互与界面展示&#xff0c;并使用Web Speech API实现行驶中路线的实时语音播报。 效果 Web Speech API简介 Web Speech API 使你能够将语音数据合并到 Web 应用程序中。Web Speech API 有两个部…

软件测试的重要一环:「性能测试」怎么做?

性能测试是软件测试中的重要一环&#xff0c;今天给大家介绍性能测试及如何使用RunnerGo完成性能测试任务。 性能测试是什么&#xff1f; 一句话概括&#xff1a;不断地通过不同场景的系统表现去探究系统设计与资源消耗之间的平衡&#xff0c;为开发人员提供消除瓶颈所需的诊…

IntelliJ IDEA 设置数据库连接全局共享

前言 在日常的软件开发工作中&#xff0c;我们经常会遇到需要在多个项目之间共享同一个数据库连接的情况。默认情况下&#xff0c;IntelliJ IDEA 中的数据库连接配置是针对每个项目单独存储的。这意味着如果你在一个项目中配置了一个数据库连接&#xff0c;那么在另一个项目中…

从零搭建开源陪诊系统:关键技术栈与架构设计

构建一个开源陪诊系统是一个涉及多种技术的复杂工程。为了让这个系统具备高效、可靠和可扩展的特点&#xff0c;我们需要从架构设计、技术栈选择到代码实现等方面进行全面的考量。本文将从零开始&#xff0c;详细介绍搭建开源陪诊系统的关键技术栈和架构设计&#xff0c;并提供…

C#中的委托、匿名方法、Lambda、Action和Func

委托 委托概述 委托是存有对某个方法的引用的一种引用类型变量。定义方法的类型&#xff0c;可以把一个方法当作另一方法的参数。所有的委托&#xff08;Delegate&#xff09;都派生自 System.Delegate 类。委托声明决定了可由该委托引用的方法。 # 声明委托类型 委托类型声…

汽车免拆诊断案例 | 2019 款奥迪 A6L 车行驶中偶发熄火

故障现象  一辆2019款奥迪A6L车&#xff0c;搭载2.0T发动机&#xff0c;累计行驶里程约为9万km。车主反映&#xff0c;车辆行驶中偶发熄火&#xff0c;故障频率较高。 故障诊断  接车后试车&#xff0c;起动发动机&#xff0c;可以正常起动着机。使用故障检测仪检测&#x…

ELK之路第一步——Elasticsearch集群的搭建以及踩坑记录

elasticSearch集群 前言一、架构二、下载三、虚拟机相关设置3.1 创建es用户3.2 为建es用户赋权sudo3.3 更换es目录所属用户 四、Elasticsearch配置文件修改4.1 修改elasticsearch.yml4.2 修改jvm.options4.3 修改jdk路径 五、启动六、启动报错七、可视化界面cerebro 前言 Elk&…

springboot080房屋租赁管理系统的设计与实现(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;房屋租赁管理系统的设计与实现 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好…

Linux 基础io_ELF_虚拟物理地址_动态库加载

1.可执行程序格式 ELF [wwshcss-ecs-178e myshell]$ ll total 56 -rw-rw-r-- 1 wws wws 92 Oct 17 19:14 file -rw-rw-r-- 1 wws wws 82 Oct 12 16:51 makefile -rw-r--r-- 1 wws wws 90 Oct 17 19:13 myfile -rwxrwxr-x 1 wws wws 20128 Oct 16 21:02 myshell -rw-r…

005:航空力学基础、无人机操纵、飞机性能

摘要&#xff1a;本文详细介绍无人机稳定性、操控性、飞机性能等概念。 一、飞机的稳定性 概念&#xff1a; 飞机的稳定性&#xff08;安定性&#xff09;&#xff0c;是指在飞机受到扰动后&#xff0c;不经飞行员操纵&#xff0c;能恢复到受扰动前的原始状态&#xff08;即原…

萤石设备视频接入平台EasyCVR私有化视频平台变电站如何实现远程集中监控?

一、方案背景 随着城市经济的发展和电力系统的改造&#xff0c;变电站的数量和规模逐渐增加&#xff0c;对变电站的安全管理和监控需求也越来越高。视频监控系统作为重要的安全管理手段&#xff0c;在变电站中起到了关键的作用。 目前青犀视频研发的萤石设备视频接入平台EasyC…

刷题 - 图论

1 | bfs/dfs | 网格染色 200. 岛屿数量 访问到马上就染色&#xff08;将visited标为 true)auto [cur_x, cur_y] que.front(); 结构化绑定&#xff08;C17&#xff09;也可以不使用 visited数组&#xff0c;直接修改原始数组时间复杂度: O(n * m)&#xff0c;最多将 visited 数…

生信软件39 - GATK最佳实践流程重构,提高17倍分析速度的LUSH流程

1. LUSH流程简介 基因组测序通常用于分子诊断、分期和预后&#xff0c;而大量测序数据在分析时间方面提出了挑战。 对于从FASTQ到VCF的整个流程&#xff0c;LUSH流程在非GVCF和GVCF模式下都大大降低了运行时间&#xff0c;30 X WGS数据耗时不到2 h&#xff0c;从BAM到VCF约需…

力扣143:重排链表

给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; L0 → L1 → … → Ln - 1 → Ln请将其重新排列后变为&#xff1a; L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … 不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交换。 示…

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

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

在 Ubuntu 上安装 OpenCV 3.2.0 的详细指南

以下步骤将指导您如何在 Ubuntu 系统上从源码编译并安装 OpenCV 3.2.0。 步骤 1&#xff1a;更新系统并安装必备工具 首先&#xff0c;更新您的系统并安装编译 OpenCV 所需的基本工具和库。 sudo apt-get update sudo apt-get upgrade sudo apt-get install build-essential…

docker 可用镜像服务地址(2024.10.25亲测可用)

1.错误 Error response from daemon: Get “https://registry-1.docker.io/v2/” 原因&#xff1a;镜像服务器地址不可用。 2.可用地址 编辑daemon.json&#xff1a; vi /etc/docker/daemon.json内容修改如下&#xff1a; {"registry-mirrors": ["https://…

C++进阶之路:日期类的实现、const成员(类与对象_中篇)

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

Qt之QCamera的简单使用

文章目录 一、相机操作相关示例1.摄像头操作内容使用示例2.摄像头信息展示使用示例3.摄像头设置切换、预览操作示例 二、相机使用个人操作理解1.相机类支持信息获取2.相机类曝光、焦点、图像处理控制信息获取3.快速启动相机设置&#xff08;各个设备处于理想状态&#xff09; 三…