4.10 @ResponseBody、@RestController
作用:方法返回的对象转换为JSON格式,并将JSON数据直接写入到输出流中,使用此注解后不会再经过视图解析器。使用该注解可以处理Ajax请求。
如果一个控制器类下的所有控制器方法都返回JSON格式数据且不进行跳转,可以使用@RestController代替@Controller,此时每个方法上的@ResponseBody都可以省略。
位置:方法上方或方法返回值前
1、编写jsp页面,发送ajax请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>ajax</title> <script src="https://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script> <script> $(function () { $("#btn").click(function () { var id = $("#id").val(); var name = $("#name").val(); $.get("/add",{"id":id,"name":name},function (data){ console.log(data); }); }); }); </script> </head> <body> id:<input id="id" type="text"/><br/> name:<input id="name" type="text"/><br/> <input id="btn" type="button" value="提交"> </body> </html>
2、由于jsp页面中引入jQuery的js文件,而SpringMVC会拦截所有资源,造成jquery.js失效,需要在SpringMVC核心配置文件中放行静态资源。
<!-- 放行静态资源 --> <mvc:default-servlet-handler />
3、编写结果实体类,该实体类会封装一个请求的结果
public class Result<T> { private boolean flag; private String message; private T data; // 省略getter/setter/构造方法 }
4、控制器
@Controller public class MyController { @GetMapping("/add") @ResponseBody public Result<Student> add(Student student){ Result<Student> result = new Result<>(); result.setFlag(true); result.setMessage("成功"); result.setData(student); return result; } }
5、SpringMVC会将Result对象转为JSON格式写入输出流,而SpringMVC默认使用的JSON转换器是jackson,需要在pom中添加jackson依赖。
<!-- jackson --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.0</version> </dependency>
4.11 静态资源映射
当在DispatcherServlet的<url-pattern>
中配置拦截 “/” 时,除了jsp文件不会拦截以外,其他所有的请求都会经过前端控制器进行匹配。此时静态资源例如css、js、jpg等就会被前端控制器拦截,导致不能访问,出现404问题。想要正常映射静态资源共有三种方案:
配置静态资源筛查器
在SpringMVC的配置文件中配置<mvc:default-servlet-handler />后,会在Spring容器中创建一个资源检查器,它对进入DispatcherServlet的URL进行筛查,如果不是静态资源,才由DispatcherServlet处理。
修改SpringMVC核心配置文件:
<mvc:default-servlet-handler/>
配置静态资源映射器
SpringMVC模块提供了静态资源映射器组件,通过<mvc:resources>
标签配置静态资源映射器,配置后的路径不会由DispatcherServlet处理。
修改SpringMVC核心配置文件:
<!--配置静态资源映射器--> <!-- mapping:配置请求的URL location:资源路径--> <mvc:resources mapping="/img/" location="/img/"/> <mvc:resources mapping="/js/" location="/js/"/>
配置默认Servlet处理静态资源
在web.xml可以配置默认Servlet处理静态资源,该Servlet由tomcat提供,它会直接访问静态资源不进行其他操作。这样就避免了使用DispatcherServlet对静态资源的拦截:
修改web.xml:
<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.jpg</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.png</url-pattern> </servlet-mapping>
4.12 @RequestBody
作用:将请求中JSON格式的参数转为JAVA对象
位置:写在方法参数前
1、AJAX请求发送JSON格式的参数
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>ajax</title> <script src="https://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script> <script> $(function () { $("#btn").click(function () { var id = $("#id").val(); var name = $("#name").val(); var data = JSON.stringify({"id":id,"name":name}); $.ajax({ url:"/add", contentType:"application/json", type:"post", data:data, success:function(result){ console.log(result); } }) }); }); </script> </head> <body> id:<input id="id" type="text"/><br/> name:<input id="name" type="text"/><br/> <input id="btn" type="button" value="提交"> </body> </html>
2、控制器
@Controller public class MyController { @PostMapping("/add") @ResponseBody public Result<Student> add(@RequestBody Student student){ Result<Student> result = new Result<>(); result.setFlag(true); result.setMessage("成功"); result.setData(student); return result; } }
五、SpringMVC文件上传
5.1 SpringMVC方式上传单个文件
SpringMVC使用框架提供的文件解析器对象,可以直接将请求体中的文件数据转为MultipartFile对象,从而省略原生上传中分析请求体的步骤。
1、导入文件上传的依赖
<!-- 文件上传 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency>
2、在SpringMVC核心配置文件配置文件解析器
<!--文件解析器,id是固定的--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--支持一次上传文件的总量100MB--> <property name="maxUploadSize" value="104865700"/> <!--文件名的编码格式--> <property name="defaultEncoding" value="utf-8"/> </bean>
3、编写上传表单
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>上传</title> </head> <body> <h3>文件上传</h3> <%-- 上传表单的提交方式必须是post --%> <%-- enctype属性为multipart/form-data,意思是不对表单数据进行编码 --%> <form action="/fileUpload" method="post" enctype="multipart/form-data"> <%-- 文件选择控件,类型是file,必须要有name属性--%> 选择文件:<input type="file" name="file"/> <input type="submit" value="上传"/> </form> </body> </html>
4、控制器
// MultipartFile参数名必须和JSP文件空间的name属性一致 @RequestMapping("/fileUpload") public String upload(MultipartFile file,HttpServletRequest request) throws ServletException, IOException { // 创建文件夹,存放上传文件 String realPath = request.getSession().getServletContext().getRealPath("/upload"); File dir = new File(realPath); if(!dir.exists()){ dir.mkdirs(); } // 将上传的数据写到文件夹的文件中 // 1.拿到上传的文件名 String filename = file.getOriginalFilename(); filename = UUID.randomUUID()+"_"+filename; // 2.创建空文件 File newFile = new File(dir,filename); // 3.将数据写入空文件中 file.transferTo(newFile); return "index"; }
5.2 上传多文件
1、创建JSP表单
<form action="/fileUpload2" method="post" enctype="multipart/form-data"> 文件1:<input type="file" name="files"/> 文件2:<input type="file" name="files"/> <input type="submit" value="上传"/> </form>
2、编写控制器接收上传请求
// MultipartFile参数名必须和JSP文件空间的name属性一致 @RequestMapping("/fileUpload2") public String upload(MultipartFile[] files,HttpServletRequest request) throws ServletException, IOException { //存放上传文件的文件夹 String realPath = request.getSession().getServletContext().getRealPath("/upload"); File dir = new File(realPath); if (dir.exists()) { dir.mkdirs(); } //遍历文件数组,将上传的文件保存到文件夹。 for (MultipartFile file : files) { String originalFilename = file.getOriginalFilename(); String fileName = UUID.randomUUID()+"_"+ originalFilename; File newFile = new File(dir, fileName); file.transferTo(newFile); } return "index"; }
5.3 异步上传
之前的上传方案,在上传成功后都会跳转页面。而在实际开发中,很多情况下上传后不进行跳转,而是进行页面的局部刷新,比如:上传头像成功后将头像显示在网页中。这时候就需要使用异步文件上传。
1、编写JSP页面,引入jQuery和jQuery表单上传工具jquery.form.js(资源已经上传)
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>上传</title> <script src="https://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script> <script src="/js/jquery.form.js"></script> </head> <body> <h3>文件上传</h3> <form id="ajaxForm" enctype="multipart/form-data" > <input type="file" name="file"/> <%-- 按钮类型不能是submit,否则会刷新页面 --%> <input type="button" value="上传头像" id="btn"/> </form> <%-- 上传头像后展示的位置 --%> <img src="/" width="100" id="img"> <script> $(function () { $("#btn").onclick(function () { // 异步提交表单 $("#ajaxForm").ajaxSubmit({ url:"/fileUpload3", type:"post", success:function (data) { $("#img").attr("src",data); } }) }) }) </script> </body> </html>
2、控制器
// MultipartFile参数名必须和JSP文件空间的name属性一致 @RequestMapping("/fileUpload3") @ResponseBody public String upload(MultipartFile file,HttpServletRequest request) throws ServletException, IOException { //存放上传文件的文件夹 String realPath = request.getSession().getServletContext().getRealPath("/upload"); File dir = new File(realPath); if (dir.exists()) { dir.mkdirs(); } //将上传的文件保存到文件夹。 String originalFilename = file.getOriginalFilename(); String fileName = UUID.randomUUID()+"_"+ originalFilename; File newFile = new File(dir, fileName); file.transferTo(newFile); //返回文件路径 return "/upload/"+fileName; }
注意:spring的版本最好是高一点的,5.2.13版本的会出问题。
5.4 跨服务器上传
由于文件占据磁盘空间较大,在实际开发中往往会将文件上传到其 他服务器中,此时需要使用跨服务器上传文件。
- 解压tomcat作为图片服务器,在tomcat的webapps下创建upload目录作为文件上传目录。
- 修改tomcat的
conf/web.xml
文件,支持跨服上传。
<servlet> <init-param> <param-name>readonly</param-name> <param-value>false</param-value> </init-param> </servlet>
3、修改tomcat的conf/server.xml
文件,修改tomcat端口,修改完开启tomcat服务器
<Connector port="8081" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
4、编写JSP上传表单(和异步上传一样)
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>上传</title> <script src="https://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script> <script src="/js/jquery.form.js"></script> </head> <body> <h3>文件上传</h3> <form id="ajaxForm" enctype="multipart/form-data" > <input type="file" name="file"/> <%-- 按钮类型不能是submit,否则会刷新页面 --%> <input type="button" value="上传头像" id="btn"/> </form> <%-- 上传头像后展示的位置 --%> <img src="/" width="100" id="img"> <script> $(function () { $("#btn").onclick(function () { // 异步提交表单 $("#ajaxForm").ajaxSubmit({ url:"/fileUpload4", type:"post", success:function (data) { $("#img").attr("src",data); } }) }) }) </script> </body> </html>
5、添加跨服上传依赖
<!-- 跨服上传 --> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-core</artifactId> <version>1.18.1</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-client</artifactId> <version>1.18.1</version> </dependency>
6、控制器
@PostMapping("/fileUpload4") @ResponseBody public String upload(MultipartFile file,HttpServletRequest request) throws ServletException, IOException { // 设置跨服上传的服务器路径 String path = "http://localhost:8081/upload/"; //获取上传的文件名 String originalFilename = file.getOriginalFilename(); String newFilename = UUID.randomUUID()+"_"+originalFilename; //跨服上传 //1、创建客户端对象 Client client = Client.create(); //2、使用客户端对象链接图片服务器 WebResource resource = client.resource(path + newFilename); //3、数据传输 resource.put(file.getBytes()); //4、返回文件路径 return path + newFilename; }
5.5 文件下载
查询所有可下载的文件
1、编写控制器方法,查询所有可下载的文件,并跳转到下载页面(这里要下载的文件是本项目中的upload目录下的文件)
//展示所有可下载的文件 @RequestMapping("/showFiles") public String showFiles(HttpServletRequest request, Model model) throws Exception { //1、获取下载的文件的路径集合(跨服务器上传的文件不能获取文件的路径集合) String path = request.getSession().getServletContext().getRealPath("/upload"); File file = new File(path); String[] files = file.list(); //将集合放到request域 model.addAttribute("files",files); return "files"; }
2、前端页面需要使用jstl标签库遍历request域中的数组,所以要添加依赖。
<!--jstl--> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
3、前端页面
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <c:forEach var="file" items="${requestScope.files}"> 点击链接下载文件: <a href="/down?fileName=${file}">${file}</a><br/> </c:forEach> </body> </html>
4、文件下载控制器
//文件下载 @RequestMapping("/download") public void down(HttpServletRequest request, HttpServletResponse response, String fileName) throws IOException { // 设置响应头(响应的是一个文件) response.setHeader("Content-Disposition","attachment;filename="+fileName); //获取文件路径 String path = request.getSession().getServletContext().getRealPath("/upload"); File file = new File(path,fileName); //获取字节输出流 ServletOutputStream os = response.getOutputStream(); //利用输出流写出文件 os.write(FileUtils.readFileToByteArray(file)); os.flush(); os.close(); }