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

SpringMVC - 06

一、Ajax

1. 概述

AjaxAsynchronous Javascript And 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 异步加载数据

使用格式

<%-- 引入 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. 单个数据

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

<%@ 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 弹窗。

后端控制层代码

// 控制层中所有返回的字符串都不会经过视图解析器,而是直接返回字符串
@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. 对象

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

<%@ 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 字符串,经过前端处理后,在页面中显示信息。

后端控制层代码

@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. 实践

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

<%@ 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>

后端控制层代码

@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 配置文件中,配置拦截器,包括使用的拦截器以及要拦截的请求。
// 第一步:自定义一个拦截器 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,表示不执行下一个拦截器(拦截)。

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

3. 实践

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

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

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

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

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 配置文件中,配置拦截器,在首页点击【主页面】超链接时,进行拦截。

<!-- 拦截器配置 -->
<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
<!-- 文件上传 -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>

2. 步骤

文件上传的步骤

  1. 导入依赖;
  2. 编写前端页面:建立表单;
  3. 编写控制类,实现文件上传;
  4. 在 Spring 配置文件中配置文件上传。
  • 前端页面

  • 控制类

@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 直接用一个字符串表示。

  • 配置文件上传
<!-- 在 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. 步骤

固定代码

@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 中删除对应的子模块。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sun 3285

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值