SSM之SpringMVC 04 —— Ajax、拦截器、文件上传和下载

系列文章

SSM之SpringMVC 01 —— SpringMVC原理及概念、Hello SpringMVC 注解版和配置版
SSM之SpringMVC 02 —— Controller和RestFul风格、转发和重定向、数据处理和乱码问题、Json
SSM之SpringMVC 03 —— 整合SSM(简单图书系统)
SSM之SpringMVC 04 —— Ajax、拦截器、文件上传和下载



8、Ajax

8.1、简介

  • Ajax 即 Asynchronous JavaScript and XML (异步的JavaScript和XML)

  • Ajax是一种无需加载整个网页,只刷新部分网页的技术

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

  • 例如百度的搜索框,在输入关键字时,JavaScript会把数据传到服务器,接着服务器会进行响应。(没有点击按钮,仅仅只是点了搜索框)

在这里插入图片描述

  • 传统的网页,想要更新内容或提交表单必须重新加载整个网页

  • 使用Ajax技术的网页,通过与服务器进行少量的数据交换就能实现异步局部更新

 

8.2、伪造Ajax

我们使用前端的iframe标签来模拟实现Ajax的效果。不太了解的可以看我另一篇博客:前端学习 01 —— HTML5 里面有讲iframe内联框架,同时还用到了DOM操作,动态修改前端,感兴趣就看另一篇 前端学习 03 —— JavaScript 02 里面有讲操作DOM对象。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Fake Ajax</title>
    <script>
        function loadPage() {
            let targetUrl = document.getElementById("url").value;
            document.getElementById("iframePosition").src = targetUrl;
        }
    </script>
</head>
<body>
    <div>
        <span>请输入要加载的网址:</span>
        <input type="text" id="url" value="https://www.baidu.com"/>
        <input type="button" value="提交" onclick="loadPage()">
    </div>
    <div>
        <h3>加载的网页:</h3>
        <iframe id="iframePosition" style="width: 100%;height: 500px;"/>
    </div>
</body>
</html>

如此,我们便可以在不重新加载整个页面的情况下,刷新部分内容。

 

8.3、jQuery.Ajax

  • jQuery库有提供Ajax,没必要用纯原生的JavaScript实现。(jQuery库不算框架,它只是包含了很多JavaScript的函数)

  • Ajax的核心是XMLHttpRequest(XHR),它提供了向服务器发送请求和解析服务器响应的接口,能够异步获取服务器数据(就是不需要整个页面刷新)

    由于这里主要是后端开发,仅需要了解Ajax部分参数。

    jQuery.ajax(...)
                部分参数:
                	url 请求地址
                	type 请求方式(GETPOST)
                	data 要发送的数据
                	success 成功后执行的回调函数(全局)
                	error 失败后执行的回调函数
                	beforeSend 发送请求前执行的回调函数
                	complete 完成后执行的回调函数
                	timeout 请求超时的时间
                	dataType 将服务器返回的数据转换为指定类型
    

     

下面我们在Spring中使用原始的HttpServletResponse处理

需要先下载jQuery包或者在线加载jQuery:https://www.jq22.com/jquery-info122

网盘里的各种环境目录下:https://pan.baidu.com/s/16fNpMkGeXZud-U58Z5y67g

提取码: q3eq

1、配置好spring-mvc和web.xml

spring-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 配置SpringMVC    -->
    <!-- 1. 扫描包  -->
    <context:component-scan base-package="com.zcy.controller"/>
    <!-- 2. 开启注解驱动   -->
    <mvc:annotation-driven/>
    <!-- 3. 静态资源默认Servlet配置  -->
    <mvc:default-servlet-handler/>

    <!-- 4. 配置JSP的视图解析器  -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- 配置DispatcherServlet -->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!-- 注意:这里要用总的Spring配置文件 -->
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 用Spring自带的乱码过滤器 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- session过期时间 15分钟    -->
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>
</web-app>

2、编写AjaxController.java,这里用原生的HttpServletResponse,将接收到的数据直接返回。

package com.zcy.controller;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RestController//使类下所有方法的返回值都是字符串
@RequestMapping("/ajax")
public class AjaxController {

    @PostMapping("/a1")
    public void ajax1(String data, HttpServletResponse resp) throws IOException {
        resp.getWriter().print(data);
    }
}

3、index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
    <script src="${pageContext.request.contextPath}/statics/js/jquery-3.5.1.min.js"></script>
    <script>
      function a1() {
        //这里也可以写成$.post(url,data(可省略),success);
        $.post({
           //这里${}不是jQuery,是操作jsp内置变量
          url:"${pageContext.request.contextPath}/ajax/a1",
          //这里$("#xxx")是jQuery,通过id选择器选myData,val()是获取其值
          data:{"data":$("#myData").val()},
          success:function (data) {
            alert(data);
          }
        })
      }
    </script>
  </head>
  <body>
    <%-- onblur 失去焦点触发事件  --%>
    <input type="text" placeholder="请输入数据" οnblur="a1()" id="myData">
  </body>
</html>

结果:可以发现,当我们输入内容后,鼠标点击输入框外面(失去焦点),就会发送请求。注意下面的类型,XHR。

在这里插入图片描述

接下来用SpringMVC的方式实现

1、新建实体类User

package com.zcy.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//使用lombok省略实体类的set、get、toString等方法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String username;
    private String password;
}

2、AjaxController.java 新增方法

@PostMapping("/a2")
public List<User> ajax2(){
    List<User> list = new ArrayList<User>();
    list.add(new User("小白","123"));
    list.add(new User("小黄","123"));
    list.add(new User("小黑","123"));
    System.out.println(list);
    return list;//由于类上有@RestController注解,会将list转为json格式返回
}

3、前端页面:ajax.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/statics/js/jquery-3.5.1.min.js"></script>
    <script>
        $(function () {
            $("#btn").click(function () {
                //这里也可以写成$.post(url,data(可省略),success);
                $.post({
                    url:"${pageContext.request.contextPath}/ajax/a2",
                    //data是从后端返回过来的json
                    success:function (data) {
                        //这里动态给表格增加内容,操作DOM。
                        let html = "";
                        for (let i = 0; i < data.length; i++){
                            html += "<tr>"+
                                    "<td>"+data[i].username+"</td>"+
                                    "<td>"+data[i].password+"</td>"+
                                    "</tr>";
                        }
                        //通过id选择器给id为content的元素追加html内容
                        $("#content").html(html);
                    }
                })

            })
        })
    </script>
</head>
<body>
    <input type="button" value="查看数据" id="btn"/>
    <table border="1">
        <thead>
            <tr><td>用户名</td><td>密码</td></tr>
        </thead>
        <tbody id="content">

        </tbody>
    </table>
</body>
</html>

结果:

在这里插入图片描述
 

9.4、注册提示效果

当我们输入完账号或者密码时,Ajax直接就将数据发到后端,后端响应后,前端立即显示提示信息。

在这里插入图片描述

1、AjaxControlle新增方法

@PostMapping("/a3")
public String ajax3(String username, String password){
    String message = "";
    if (username != null){
        if (username.equals("admin"))
            message = "OK";
        else
            message = "ERROR";
    }
    if (password != null){
        if (password.equals("123456"))
            message = "OK";
        else
            message = "ERROR";
    }
    return message;
}

2、前端 ajax2.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/statics/js/jquery-3.5.1.min.js"></script>
    <script>
        function username() {
            $.post({
                url:"${pageContext.request.contextPath}/ajax/a3",
                data:{"username":$("#username").val()},
                success:function (data) {
                    if (data.toString()==="OK")
                        $("#info1").css("color", "green");
                    else
                        $("#info1").css("color", "red");
                    $("#info1").html(data);
                }
            })
        }
        function password() {
            $.post({
                url:"${pageContext.request.contextPath}/ajax/a3",
                data:{"username":$("#password").val()},
                success:function (data) {
                    if (data.toString()==="OK")
                        $("#info2").css("color", "green");
                    else
                        $("#info2").css("color", "red");
                    $("#info2").html(data);
                }
            })
        }
    </script>
</head>
<body>
    账号:<input type="text" id="username" οnblur="username()"/><span id="info1"></span><br/>
    密码:<input type="text" id="password" οnblur="password()"/><span id="info2"></span>
</body>
</html>

 

9、拦截器

9.1、简介

SpringMVC的拦截器(Interceptor)类似于Servlet开发中的过滤器(Filter),用于对Controller进行预处理以及执行后处理。

  • 拦截器是SpringMVC框架自身才有的,必须使用了SpringMVC框架才能用
  • 过滤器是Servlet规范中的一部分,任何JavaWeb工程都能用,SpringMVC也可以用过滤器(前面Json乱码处理就是用了过滤器)
  • 拦截器是AOP思想的具体应用,横切进去,不影响原有业务。
  • 对于过滤器,在web.xml中将拦截器的url-pattern设置为/*后,可以对任何资源进行过滤。
  • 对于拦截器,它只会拦截要访问Controller的请求,如果访问jsp/html/js等是不会拦截的。
     

9.2、自定义拦截器

1、MyInterceptor.java

package com.zcy.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {
    //返回值 确定该请求是否放行,若为false,则无法访问到后面的Controller
    public boolean preHandle(
            HttpServletRequest req, HttpServletResponse resp, Object handler
                            ) throws Exception {
        System.out.println("=====处理里前=====");
        return true;
    }

    //一般postHandle和afterCompletion是来打印日志的
    public void postHandle(
            HttpServletRequest req, HttpServletResponse resp, Object handler, ModelAndView modelAndView
                          ) throws Exception {
        System.out.println("=====处理后=====");
    }

    public void afterCompletion(
            HttpServletRequest req, HttpServletResponse resp, Object handler, Exception ex
                               ) throws Exception {
        System.out.println("=====清理后=====");
    }
}

2、InterceptorController.java

package com.zcy.controller;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/interceptor")
public class InterceptorController {

    @GetMapping("/i1")
    public String interceptor1(){
        System.out.println("执行InterceptorController");
        return "Hello,Interceptor";
    }
}

3、在spring-mvc.xml中配置拦截器

<!-- 拦截器配置:AOP的应用  -->
<mvc:interceptors>
    <mvc:interceptor>
        <!-- 拦截的位置,/admin/**表示拦截/admin/下所有,这里是拦截项目下所有的 -->
        <!-- 如果是/admin/*,则只拦截到/admin下一层,对于/admin/add/user就不会拦截 -->
        <mvc:mapping path="/**"/>
        <!-- 用哪个拦截器 -->
        <bean class="com.zcy.interceptor.MyInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

结果:

在这里插入图片描述
 

9.3、用户登录拦截

现在用拦截器实现之前过滤器做的事,只有用户成功登录才能进入主页,如果注销后,直接输入主页的URL是无法进入的。

1、编写拦截器并配置

package com.zcy.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginInterceptor implements HandlerInterceptor {
    public boolean preHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler
                            ) throws Exception {
        boolean flag = false;
        String requestURI = request.getRequestURI();

        if (requestURI.contains("toLogin"))//如果是去登录界面,则直接放行
            return true;
        if (requestURI.contains("login"))//如果是去登录界面后台的Controller,则放行
            return true;
        //说明用户已经登录过,放行。如果用户注销了,则为null,拦截。
        if (request.getSession().getAttribute("userName") != null)
            return true;

        //拦截就会重定向到Home界面
        request.getRequestDispatcher("/WEB-INF/jsp/home.jsp").forward(request, response);
        return false;
    }
}

spring-mvc.xml

<mvc:interceptor>
    <!-- 拦截 /user/下所有请求 -->
    <mvc:mapping path="/user/**"/>
    <bean class="com.zcy.interceptor.LoginInterceptor"/>
</mvc:interceptor>

2、编写Controller

package com.zcy.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("/user")
public class UserController {

    //跳转到登陆界面
    @RequestMapping("/toLogin")
    public String toLogin(){
        System.out.println("toLogin");
        return "login";
    }

    //登录成功跳转到主页面
    @RequestMapping("/login")
    public String login(String username, String password, HttpSession session){
        System.out.println("login");
        if (username != null && username.length() > 0){
            session.setAttribute("userName", username);
            return "main";
        }
        else
            return "login";
    }

    //跳转到主页面
    @RequestMapping("/main")
    public String main(){
        return "main";
    }

    //注销
    @RequestMapping("/logout")
    public String logout(HttpSession session, Model model){
        session.removeAttribute("userName");
        model.addAttribute("message", "您已注销");
        return "home";
    }
}

3、编写前端

home.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Home</title>
</head>
<body>
    <h1>首页</h1>
    <span>${message}</span>
    <h2><a href="${pageContext.request.contextPath}/user/main">进入主页</a><h2/>
    <h2><a href="${pageContext.request.contextPath}/user/toLogin">进入登录页面</a></h2>

</body>
</html>

main.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Main</title>
</head>
<body>
    <h1>主页面</h1>
    <h3>欢迎用户:${userName}</h3>
    <a href="${pageContext.request.contextPath}/user/logout">注销</a>
</body>
</html>

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Login</title>
</head>
<body>
    <h1>登录界面</h1>
    <form action="${pageContext.request.contextPath}/user/login" method="post">
        用户名:<input type="text" name="username"><br/>
        密&nbsp;&nbsp;&nbsp;码:<input type="text" name="password"/><br/>
        <input type="submit" value="登录">
    </form>
</body>
</html>

结果:在未登录或已注销情况下,无法进入主页。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
 

10、文件下载和上传

文件下载和上传是项目中常见的功能,现在用SpringMVC来实现。

10.1、文件上传

前端表单必须设置为POST,并将enctype设置为multipart/form-data,只有这样浏览器才会把用户选择的文件以二进制方式发给服务器。

1、导包

<!--文件上传-->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>
<!--servlet-api导入高版本的-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>

2、配置spring-mvc.xml,添加bean:multipartResolver(id必须为multipartResolver)

<!--文件上传配置-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	<!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
	<property name="defaultEncoding" value="utf-8"/>
	<!-- 上传文件大小上限,单位为字节(10485760=10M) -->
	<property name="maxUploadSize" value="10485760"/>
	<property name="maxInMemorySize" value="40960"/>
</bean>

CommonsMultipartFile 常用方法:

  • String getOriginalFilename():获取上传文件的原名
  • InputStream getInputStream():获取文件流
  • void transferTo(File dest):将上传文件保存到一个目录文件中

3、前端页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>

  <body>
    <%-- enctype="multipart/form-data"以二进制方式处理表单数据  --%>
    <form action="/upload" enctype="multipart/form-data" method="post">
      <input type="file" name="file"/>
      <input type="submit" value="upload">
    </form>
    <a href="/download">点击下载</a>
  </body>
</html>

4、Controller

package com.zcy.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;

@RestController
@RequestMapping("/file")
public class FileController {
    //方式一
    //@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象
    //批量上传CommonsMultipartFile则为数组即可
    @RequestMapping("/upload1")
    public String fileUpload(@RequestParam("file") CommonsMultipartFile
            file , HttpServletRequest request) throws IOException {
        //获取文件名 : file.getOriginalFilename();
        String uploadFileName = file.getOriginalFilename();
        //如果文件名为空,直接回到首页!
        if ("".equals(uploadFileName)){
            return "redirect:/index.jsp";
        }
        System.out.println("上传文件名 : "+uploadFileName);
        //上传路径保存设置
        String path = request.getRealPath("/upload");
        //如果路径不存在,创建一个
        File realPath = new File(path);
        if (!realPath.exists()){
            realPath.mkdir();
        }
        System.out.println("上传文件保存地址:"+realPath);
        InputStream is = file.getInputStream(); //文件输入流
        OutputStream os = new FileOutputStream(new
                File(realPath,uploadFileName)); //文件输出流
        //读取写出
        int len=0;
        byte[] buffer = new byte[1024];
        while ((len=is.read(buffer))!=-1){
            os.write(buffer,0,len);
            os.flush();
        }
        os.close();
        is.close();
        return "redirect:/index.jsp";
    }

    //方式二:采用file.Transto 来保存上传的文件
    @RequestMapping("/upload2")
    public String fileUpload2(@RequestParam("file") CommonsMultipartFile
            file, HttpServletRequest request) throws IOException {
        //上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        File realPath = new File(path);
        if (!realPath.exists()){
            realPath.mkdir();
        }
        //上传文件地址
        System.out.println("上传文件保存地址:"+realPath);
        //通过CommonsMultipartFile的方法直接写文件(注意这个时候)
        file.transferTo(new File(realPath +"/"+
                file.getOriginalFilename()));
        return "redirect:/index.jsp";
    }
}

 

10.2、文件下载

文件下载步骤:

  • 设置 response 响应头
  • 读取文件 – InputStream
  • 写出文件 – OutputStream
  • 执行操作
  • 关闭流 (先开后关)
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response ,HttpServletRequest
                        request) throws Exception{
    //要下载的图片地址
    String path = request.getServletContext().getRealPath("/upload");
    String fileName = "123.jpg";
    //1、设置response 响应头
    response.reset(); //设置页面不缓存,清空buffer
    response.setCharacterEncoding("UTF-8"); //字符编码
    response.setContentType("multipart/form-data"); //二进制传输数据
    //设置响应头
    response.setHeader("Content-Disposition",
                       "attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));
    File file = new File(path,fileName);
    //2、 读取文件--输入流
    InputStream input=new FileInputStream(file);
    //3、 写出文件--输出流
    OutputStream out = response.getOutputStream();
    byte[] buff =new byte[1024];
    int index=0;
    //4、执行 写出操作
    while((index= input.read(buff))!= -1){
        out.write(buff, 0, index);
        out.flush();
    }
    out.close();
    input.close();
    return "ok";
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

老板来碗小面加蛋~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值