SpringMVC:Ajax、拦截器、文件上传、文件下载

文章目录

  • [SpringMVC - 06](#SpringMVC - 06)
  • 一、Ajax
    • [1. 概述](#1. 概述)
    • [2. Ajax 异步加载数据](#2. Ajax 异步加载数据)
      • [1. 单个数据](#1. 单个数据)
      • [2. 对象](#2. 对象)
    • [3. 实践](#3. 实践)
    • [4. 总结](#4. 总结)
  • 二、拦截器
    • [1. 概述](#1. 概述)
    • [2. 实现](#2. 实现)
    • [3. 实践](#3. 实践)
    • [4. 总结](#4. 总结)
  • 三、文件上传:Upload
    • [1. 准备工作](#1. 准备工作)
    • [2. 步骤](#2. 步骤)
    • [3. 效果](#3. 效果)
  • 四、文件下载:Download
    • [1. 步骤](#1. 步骤)
    • [2. 效果](#2. 效果)
    • [3. 总结](#3. 总结)
  • 注意:

SpringMVC - 06

一、Ajax

1. 概述

AjaxA synchronous J avascript A nd XML(异步 JavaScript 和 XML)

  1. 使用 Ajax 技术,网页能够快速地将增量更新呈现在用户界面上,而不需要刷新整个页面,这使得程序能够更快地回应用户的操作;

  2. Ajax 不是一种新的编程语言,而是一种用于创建更好更快 以及交互性更强 的 Web 应用程序的技术

  3. 使用 Ajax 的最大优点 ,就是能在不更新整个页面的前提下维护数据,即在不需要刷新页面的情况下,就可以产生局部刷新的效果。

需要用到 jQuery 实现 Ajax,它是一个快速、简洁的 JavaScript 框架 ,具有 js 的大量函数,引入 jQuery 的方法

  1. 进入 jQuery 官网:点此进入,找到 [Download the uncompressed, development jQuery 3.7.0]
  2. 点击链接,出现 js 代码,通过 Ctrl + S 保存到下载的位置;
  3. 在 web 文件夹新建 static 文件夹,将下载好的 jQuery 框架导入。

2. Ajax 异步加载数据

使用格式

jsp 复制代码
<%-- 引入 jQuery 框架 --%>
<script src="${pageContext.request.contextPath}/static/jquery-3.7.0.js"></script>

<script>
    function xxx() {
        $.post({
            url:"${pageContext.request.contextPath}/请求地址",
            data:{"后端参数名":$("#前端参数名").val()},
            success:function (data) {
                成功之后执行的回调函数;
            }, 
            error:function (data) {
                失败之后执行的回调函数;
            }
        })
    }
</script>

注意

  • script 标签必须 成对,并且放在 body 的最后
  • $.post() 相当于 jQuery.post() ,使用时一定要提前引入 jQuery 框架;
  • $.post放在函数内 ,其中的参数 有:
    • url请求地址
    • data :前端要发送给后端的数据 ,没有数据时可以省略
    • success成功 之后执行的回调函数注意这里回调函数中参数 data 与上面的 data 不是同一个,这里是指后端传递给前端的数据;
    • error失败 之后执行的回调函数
  • 之前 是由后端控制转发或重定向,完成视图的跳转,而现在是由前端 Ajax 来控制,后端只需要提供数据(JSON 字符串)即可,Ajax 把主动权交给前端。

1. 单个数据

举例:当用户输入一个信息后,进行弹窗提示。

jsp 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试</title>

    <script src="${pageContext.request.contextPath}/static/jquery-3.7.0.js"></script>

    <script>
        function a() {
            $.post({
                url:"${pageContext.request.contextPath}/mon/a1",
                data:{"name":$("#username").val()},
                success:function (data) {
                    alert(data);
                }
            })
        }
    </script>
</head>
<body>
<h1>
    <%-- 失去焦点时,发起一个请求(携带信息)到后台 --%>
    用户名:<input type="text" id="username" οnblur="a()">
</h1>
</body>
</html>

注意

  1. 前端向后端传递单个数据时,data 必须 是键值对(JSON 字符串)的形式,这里的 name后端的参数名username前端的参数名
  2. 在 Ajax 中,前端参数的值(例如用户输入的信息)可以通过 $("#前端参数名").val() 的方式取到;
  3. console.log 可以在控制台输出信息
  4. alert 代表弹窗
  5. onblur 代表失去焦点时,执行函数;
  6. 该代码可以执行的操作 是:在前端页面中,用户输入用户名信息,失去焦点时,执行函数 a() ,发起 /mon/a1 请求并携带前端数据给后端,后端执行成功(success)后,执行回调函数,此时回调函数的参数 data 为后端返回的数据,进行 alert 弹窗。

后端控制层代码

java 复制代码
// 控制层中所有返回的字符串都不会经过视图解析器,而是直接返回字符串
@RestController
@RequestMapping("/mon")
public class AjaxController {

    @RequestMapping("/a1")
    public String a1(String name) {
        System.out.println(name);

        if ("Sun3285".equals(name)) {
            return "true";
        } else {
            return "false";
        }
    }
}

执行

2. 对象

举例:当用户点击【加载数据】按钮时,加载后台中的数据。

jsp 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试</title>

    <script src="${pageContext.request.contextPath}/static/jquery-3.7.0.js"></script>

    <script>
        $(function () {
            $("#btn").click(
                function () {
                    $.post({
                        url:"${pageContext.request.contextPath}/mon/a2",
                        success:function(data) {
                            console.log(data);
                            var obj = JSON.parse(data);
                            console.log(obj);
                            var html = "";

                            for (let i = 0; i < obj.length; i++) {
                                html += "<tr>" +
                                    "<td>" + obj[i].id + "</td>" +
                                    "<td>" + obj[i].name + "</td>" +
                                    "<td>" + obj[i].sex + "</td>" +
                                    + "</tr>";
                            }

                            $("#content").html(html);
                        }
                    })
                }
            )
        })
    </script>
</head>
<body>
<input type="button" value="加载数据" id="btn">
<table>
    <tr>
        <td>编号</td>
        <td>姓名</td>
        <td>性别</td>
    </tr>
    <tbody id="content">
        <%-- 后台数据 --%>
    </tbody>
</table>
</body>
</html>

注意

  1. 重点看 Ajax 部分,因为前端不给后端传递数据,因此省略了 data;
  2. 后端传递给前端的数据 data 为 JSON 字符串 ,因此要先通过 JSON.parse(data) 解析为 JavaScript 对象,可以通过 console.log 来看到解析前后的结果;
  3. 该代码可以执行的操作 是:在前端页面中,用户点击【加载数据】按钮时,通过 $("#btn").click 可以捕捉到,然后执行函数,发起 /mon/a2 请求给后端,后端执行成功(success)后,执行回调函数,此时回调函数的参数 data 为后端返回的数据,即一个 JSON 字符串,经过前端处理后,在页面中显示信息。

后端控制层代码

java 复制代码
@RestController
@RequestMapping("/mon")
public class AjaxController {

    @RequestMapping("/a2")
    public String a2() throws JsonProcessingException {
        ArrayList<Student> studentList = new ArrayList<Student>();
        Student s1 = new Student(1, "Sun1234", "男");
        Student s2 = new Student(2, "Sun3285", "男");
        Student s3 = new Student(3, "Sun4399", "女");
        Collections.addAll(studentList, s1, s2, s3);
        
        // 后端传递给前端的是一个 JSON 字符串
        return new ObjectMapper().writeValueAsString(studentList);
    }
}

执行

3. 实践

需求 :输入用户名和密码时,动态显示输入信息是否正确。

jsp 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>

    <script src="${pageContext.request.contextPath}/static/jquery-3.7.0.js"></script>

    <script>
        function login1() {
            $.post({
                url:"${pageContext.request.contextPath}/mon/login1",
                data:{"username":$("#username").val()},
                success:function (data) {
                    console.log(data);
                    if (data === "OK") {
                        $("#usernameInfo").css("color", "green");
                    } else {
                        $("#usernameInfo").css("color", "red");
                    }
                    $("#usernameInfo").html(data);
                }
            })
        }
        function login2() {
            $.post({
                url:"${pageContext.request.contextPath}/mon/login2",
                data:{"username":$("#username").val(), "password":$("#password").val()},
                success:function (data) {
                    console.log(data);
                    if (data === "OK") {
                        $("#passwordInfo").css("color", "green");
                    } else {
                        $("#passwordInfo").css("color", "red");
                    }
                    $("#passwordInfo").html(data);
                }
            })
        }
    </script>
</head>
<body>
<p>
   用户名:<input type="text" id="username" οnblur="login1()"><span id="usernameInfo"></span>
    <br>
   密  码:<input type="text" id="password" οnblur="login2()"><span id="passwordInfo"></span>
</p>
</body>
</html>

后端控制层代码

java 复制代码
@RestController
@RequestMapping("/mon")
public class AjaxController {

    @RequestMapping("/login1")
    public String login1(String username) {
        if ("Sun3285".equals(username)) {
            return "OK";
        } else {
            return "用户名输入有误";
        }
    }

    @RequestMapping("/login2")
    public String login2(String username, String password) {
        String result = login1(username);

        if ("OK".equals(result)) {
            if ("123456".equals(password)) {
                return "OK";
            } else {
                return "密码输入有误";
            }
        } else {
            return result;
        }
    }
}

注意 :判断密码是否正确的前提是:先要判断用户名是否正确。

执行

4. 总结

  1. js 中三个等号 为严格等于,指数据类型都要相等;
  2. 之前是由后端控制转发或重定向,完成视图的跳转,而现在是由前端 Ajax 来控制,后端只需要提供数据(JSON 字符串)即可,Ajax 把主动权交给前端;
  3. 前后端之间交互数据是以 JSON 字符串的形式;
  4. 标签中 id 和 name 的区别
    • id 一般用于 css 和 js 中引用 ,name 用于表单提交只有加了 name 属性的标签元素才会提交到服务器;
    • id 是唯一 的标识符,不允许有重复值,可以通过它的值来获得对应的 html 标签对象,相当于人的身份证具有唯一性;
    • name 是控件的名字,当需要把控件所关联的数据传递到数据库时,就必须要设置 name 属性,相当于人的姓名,可以重名,用 value 可以设置控件的默认值。

二、拦截器

1. 概述

拦截器AOP 思想 的具体应用,拦截器拦截 的是请求,不拦截静态资源。

功能:用于对 SpringMVC 处理器进行预处理和后处理,可以自己定义一些拦截器来实现特定的功能。

过滤器和拦截器的区别

  • 过滤器

    1. 是 servlet 规范中的一部分,任何 JavaWeb 工程中都可以使用;
    2. 在 url-pattern 中配置了 /* 之后,可以对所有要访问的资源进行过滤;
    3. web.xml 中进行配置。
  • 拦截器

    1. 拦截器是 SpringMVC 框架的,只有使用了 SpringMVC 框架的工程才能使用;
    2. 拦截器只会拦截访问的控制器方法(控制类中的方法),如果访问的是 jsp/html/css/image/js 是不会进行拦截的;
    3. 在 Spring 配置文件中进行配置(因为拦截器是 SpringMVC 框架独有的)。

2. 实现

实现拦截器的步骤

  1. 自定义一个拦截器,实现 HandlerInterceptor 接口,重写方法;
  2. 在 Spring 配置文件中,配置拦截器,包括使用的拦截器以及要拦截的请求。
java 复制代码
// 第一步:自定义一个拦截器 MyInterceptor,实现 HandlerInterceptor 接口,重写方法
public class MyInterceptor implements HandlerInterceptor {

    // 处理前
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return false;
    }

    // 处理后
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    // 清理
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

注意 :preHandle 方法中,返回值为 true,表示 执行下一个拦截器(放行 ),返回值为 false,表示 不执行下一个拦截器(拦截)。

xml 复制代码
<!-- 第二步:在 Spring 配置文件中,对拦截器进行配置 -->
<mvc:interceptors>
    <mvc:interceptor>
        <!-- /** 表示所有请求都会经过拦截器 -->
        <mvc:mapping path="/**"/>
        <!-- 指定使用的拦截器 -->
        <bean class="com.Sun3285.config.MyInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

3. 实践

需求:登录判断验证,如果进行了登录操作,并且用户名和密码正确,才可以进入首页,否则不能直接进入首页。

拦截前:可以进入登录界面进行登录操作,输入正确的用户名和密码,进入主界面,也可以在首页上直接点击超链接,进入主界面。

点击【主页面】时使用拦截器拦截,拦截的依据是:是否在 Session 中存放了指定的信息,因为只有登录成功,才会在 Session 中存放指定信息 usernameID。

第一步:自定义拦截器,如果 Session 中存放了指定的信息(进行了登录操作),就放行,否则,跳转到登录页面。

java 复制代码
public class LoginInterceptor implements HandlerInterceptor {

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        if (session.getAttribute("usernameID") != null) {
            return true;
        }

        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
        return false;
    }
}

第二步:在 Spring 配置文件中,配置拦截器,在首页点击【主页面】超链接时,进行拦截。

xml 复制代码
<!-- 拦截器配置 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/toMain"/>
        <bean class="com.Sun3285.config.LoginInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

运行结果

  • 没有进行登录操作,直接在首页点击【主页面】时,拦截器拦截,并跳转到登录页面。
  • 进行了登录 ,再在首页点击【主页面】时,可以直接跳转到主页面,因为此时 Session 中存放了指定的信息,拦截器放行
  • 此时关闭浏览器,重新打开,再在首页点击【主页面】时,拦截器拦截,并跳转到登录页面,因为此时拦截器得到的 session 为新的 session,其中没有存放指定的信息 usernameID。

4. 总结

  1. 在 WEB-INF 下的所有页面或者资源,只能通过 controller 或 servlet 进行访问;
  2. 请求转发可以 访问 WEB-INF 下的所有页面或者资源,重定向不可以
  3. 可以在控制类的方法参数中直接拿到一些对象,如:HttpSession session、HttpServletRequest request、HttpServletResponse response 等,然后在方法内进行进一步操作。

三、文件上传:Upload

1. 准备工作

如果想使用 Spring 的文件上传功能,需要在上下文中配置 MultipartResolver。

为了能够上传文件,前端表单要求

  1. 必须将表单的 method 设置为 POST;
  2. 将 enctype 设置为 multipart/form-data,这样,浏览器才会把用户选择的文件以二进制数据发送给服务器。

后端要求

  1. 导入依赖:commons-fileupload
xml 复制代码
<!-- 文件上传 -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>

2. 步骤

文件上传的步骤

  1. 导入依赖;
  2. 编写前端页面:建立表单;
  3. 编写控制类,实现文件上传;
  4. 在 Spring 配置文件中配置文件上传。
  • 前端页面
  • 控制类
java 复制代码
@Controller
public class FileController {
    // @RequestParam("file") 将 name="file" 控件得到的文件封装为 CommonsMultipartFile 对象
    @RequestMapping("/upload")
    public String uplode(@RequestParam("file") CommonsMultipartFile file,
                         HttpServletRequest request) throws IOException {

        // 获取文件名
        String uploadFilename = file.getOriginalFilename();

        // 上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        File realPath = new File(path);
        if (!realPath.exists()) {
            realPath.mkdir();
            // realPath.mkdirs();
        }

        // 输出上传文件地址
        System.out.println("上传文件保存地址:" + realPath);

        // 通过 CommonsMultipartFile 的方法直接写文件
        file.transferTo(new File(realPath + "/" + uploadFilename));

        return "redirect:/index.jsp";
    }
}

注意 :这部分代码可以直接使用只需要 按需求改一下上传路径 path 即可,其中 upload 为新创建的文件夹,存放上传的文件。这里也可以把上传路径 path 直接用一个字符串表示。

  • 配置文件上传
xml 复制代码
<!-- 在 Spring 配置文件中配置文件上传 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 请求的编码格式 -->
    <property name="defaultEncoding" value="utf-8"/>
    <!-- 上传文件大小上限,可以省略,单位为字节(1MB=1048576字节) -->
    <property name="maxUploadSize" value="10485760"/>
</bean>

3. 效果

文件上传后,控制台输出上传文件的地址,并且可以找到已上传的文件


四、文件下载:Download

1. 步骤

固定代码

java 复制代码
@Controller
public class FileController {

    @RequestMapping("/download")
    public String download(HttpServletRequest request,
                           HttpServletResponse response) throws IOException {
        // 要下载的文件地址和文件名
        String path = request.getServletContext().getRealPath("/files");
        String fileName = "导入的依赖总结.txt";
        File file = new File(path, fileName);

        // 设置 response 响应头
        response.reset();   // 设置页面不缓存,清空 buffer
        response.setCharacterEncoding("utf-8");  // 字符编码
        response.setContentType("multipart/form-data");  // 二进制传输数据

        // 设置下载文件
        response.setHeader("Content-Disposition",
                "attachment;fileName=" + URLEncoder.encode(fileName, "utf-8"));

        // 读取文件:输入流
        InputStream is = new FileInputStream(file);
        InputStream bis = new BufferedInputStream(is);

        // 写出文件:输出流
        OutputStream os = response.getOutputStream();
        OutputStream bos = new BufferedOutputStream(os);

        // 下载操作(读取、写出)
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = bis.read(buffer)) != 0) {
            bos.write(buffer, 0, len);
            bos.flush();
        }

        // 关闭资源
        bos.close();
        bis.close();

        return null;
    }
}

注意

  1. 这部分代码可以直接使用只需要 按需求改一下要下载的文件地址和文件名,其中
    • 被下载文件的地址:这里是放在了 web 文件夹下的 files 文件夹中,最后会在生成的 out 文件夹下的子项目中;
    • 文件名:被下载的文件名。
  2. 记得最后要关闭资源,后开先关

2. 效果

无法下载文件,后端代码执行成功

判断应该是浏览器阻止了下载,更换浏览器再次尝试,可以下载成功

3. 总结

  • 上传路径 path 和被下载的文件地址 path 也都可以 直接用一个字符串表示:
    • 上传路径 path 一般通过 request.getServletContext().getRealPath("/upload") 来将文件上传到服务器生成的 out 目录下的子项目中的 upload 包(如果没有 upload 包,会自动新建)中;
    • 下载时,要提前在服务器中准备好被下载的文件,可以在 web 包下建立 files 包,用来存放被下载的文件,此时,被下载的文件地址 path 可以由 request.getServletContext().getRealPath("/files") 得到。
  • 上传 是用户通过前端表单操作将文件上传到服务器;
  • 下载 是用户点击前端超链接将服务器中的文件下载到浏览器(客户端本地文件)。

注意:

  1. jQuery 官网https://jquery.com/download/
  2. 需要将 JavaScript 的版本变为 6,方法如下:
  1. 拦截器和文件上传的配置都是 在 Spring 的配置文件中,因为都是基于 SpringMVC 框架的。
  2. 搭建完环境,要先测试一下能不能运行,环境是否有问题。
  3. 删除项目中的子模块方法 :选中子模块,右键选择 Remove Module 移除模块,然后再右键删除文件夹,注意 :要在父模块的 pom.xml 中删除对应的子模块。
相关推荐
码农派大星。几秒前
Spring Boot 配置文件
java·spring boot·后端
顾北川_野8 分钟前
Android 手机设备的OEM-unlock解锁 和 adb push文件
android·java
江深竹静,一苇以航10 分钟前
springboot3项目整合Mybatis-plus启动项目报错:Invalid bean definition with name ‘xxxMapper‘
java·spring boot
confiself26 分钟前
大模型系列——LLAMA-O1 复刻代码解读
java·开发语言
Wlq041531 分钟前
J2EE平台
java·java-ee
XiaoLeisj37 分钟前
【JavaEE初阶 — 多线程】Thread类的方法&线程生命周期
java·开发语言·java-ee
鹿屿二向箔1 小时前
基于SSM(Spring + Spring MVC + MyBatis)框架的汽车租赁共享平台系统
spring·mvc·mybatis
豪宇刘1 小时前
SpringBoot+Shiro权限管理
java·spring boot·spring
Elaine2023911 小时前
02多线程基础知识
java·多线程
gorgor在码农1 小时前
Redis 热key总结
java·redis·热key