概述
SpringMVC框架学习笔记(三)
- 六、数据传递
- 6.1 请求转发传递数据
- (1)传统方式:HttpServletRequest请求转发
- (2)使用`ModelAndView`请求转发(底层ModelMap)
- 对`addObject`源码查看
- (3)String作为请求转发返回类型传递数据(ModelMap,model和map)
- 6.2 重定向传递数据
- 6.3 会话和应用范围传递数据
- 6.4 防止重复提交
- 七、数据绑定
- 7.1 简单数据绑定
- (1)`HttpServletRequest`需要自己完成转换过程
- (2)`@RequestParam`注解:功能强大
- 初始方式
- 简写方式:推荐
- 7.2 占位符数据绑定
- 7.3 自定义对象数据绑定
- (1)单个对象的绑定:
- (2)关联对象:不同的属性
- 1. 关联对象classInfo:(不同属性)
- 2. 关联对象group:(相同属性:使用类中的属性进行区分,并且直接建立关联关系)
- 3. 同属性问题(推荐):可以通过第三方建立关联对象属性进行区分
- 7.4 Map类型的数据绑定
- 八、文件上传和下载
- 8.1 文件上传的准备
- 8.2 文件上传的步骤
- (1)新建表单
- (2)配置处理`multipart/form-data`解析器
- (3)控制器数据绑定
- 8.3 文件上传“套路”
- 8.4 文件上传和下载
- (1)文件上传保存数据(模拟操作)
- (2)下载页面
- 九、静态资源访问
- (1)使用服务器默认的排除方案
- (2)建立静态资源的映射路径
六、数据传递
SpringMVC应用中,我们经常需要在Controller将数据传递到JSP页面,除了可以通过HttpServletRequest域传递外,Spring
MVC还提供了两个Api,分别为Model接口和ModelMap类。
6.1 请求转发传递数据
6.1使用的结果页代码:
<body>
<h3>EL表达式</h3>
<h3>${requestScope.bookName}</h3>
<h3>${requestScope["123"]}</h3>
<h3>${integer}</h3>
<h3>${string}</h3>
<h3>${requestScope.dog.dogName}</h3>
</body>
可以使用
HttpServletRequest
传递数据,底层也是使用该类进行数据传递,但是进行封装
(1)传统方式:HttpServletRequest请求转发
@Controller
@RequestMapping("forword")
public class ForwordController {
@Autowired
private HttpServletRequest request;
/**JavaWeb的形式*/
@GetMapping("/data")
public String m1(){
request.setAttribute("bookName","北斗");
request.setAttribute("123",999.9);//123特殊字符
request.setAttribute("integer",888);//integer不是关键字
request.setAttribute("string","羽化");//string不是关键字
Dog dog = new Dog();
dog.setDogName("黑皇");
request.setAttribute("dog",dog);
return "result01";
}
}
(2)使用ModelAndView
请求转发(底层ModelMap)
这是SpringMVC独有的ModelAndView对象,通过声明一个ModelAndView类型的对象,就可以使用这个对象来传值或者是指定跳转的页面路径。采用addObject()方法,来规定key和value
mav.addObject()
:当存储的数据没有指定类,默认是其对应引用数据类型的首字母小写
比如:mav.addObject("大圣");
//String->string,key值为"string"
若果后面还有和此数据类型相同的数据
mav.addObject("凌霄");
//String->string
那么此时因为二者key值相同,所以显示的数据后者覆盖前者为"凌霄"
@GetMapping("/data")
public ModelAndView m2(){
ModelAndView mav = new ModelAndView();
mav.setViewName("result01");
mav.addObject("bookname","九天十地");
mav.addObject("123", 666);
/**当存储的数据没有指定类,默认是其对应引用数据类型的首字母小写*/
mav.addObject(666);//int->Integer->integer
mav.addObject("大圣");//String->string
mav.addObject("凌霄");//String->string
Dog d1 = new Dog();
d1.setDogName("啸天");
mav.addObject(d1);//d1->Dog->dog
return mav;
}
对addObject
源码查看
public ModelAndView addObject(Object attributeValue) {
this.getModelMap().addAttribute(attributeValue);
return this;
}
public ModelAndView addAllObjects(@Nullable Map<String, ?> modelMap) {
this.getModelMap().addAllAttributes(modelMap);
return this;
}
//调用this.getModelMap()返回ModelMap的类型,提供存储功能addAttribute(attributeName, attributeValue)
public ModelMap getModelMap() {
if (this.model == null) {
this.model = new ModelMap();//ModelMap进行实例化,形参构造方法就可以进行实例化
}
return this.model;
}
注意:此处代码用gerclass()
方法查看返回值类型就是ModelAndView
.
其底层用ModelMap实现
(3)String作为请求转发返回类型传递数据(ModelMap,model和map)
图片来自
- ModelMap
@GetMapping("/data03")
public String m3(ModelMap modelMap){
System.out.println("实际的堆内置类型"+modelMap.getClass());
modelMap.addAttribute("bookname","柳树");
modelMap.addAttribute("123", 999);
/**当存储的数据没有指定类,默认是其对应引用数据类型的首字母小写*/
modelMap.addAttribute(666);//int->Integer->integer
modelMap.addAttribute("二师兄");//String->string
modelMap.addAttribute("吃猪肉");//String->string
Dog d1 = new Dog();
d1.setDogName("啸天");
modelMap.addAttribute(d1);//d1->Dog->dog
return "result01";
}
ModelMap是一个类,继承LinkedMap,因此为Map结构,可以使用Key/Value形式存储值。
源码:
public class ModelMap extends LinkedHashMap<String, Object>
- Map
@GetMapping("/data04")
public String m4(Map<String,Object> map){//BindingAwareModelMap
System.out.println("实际的堆内置类型:"+map.getClass());
//Map中提供的方法比较少,尽量少用
map.put("bookName","柳树Map");
map.put("123",7777.7);
map.put("integer",666);
map.put("string","狠人");
Dog d1 = new Dog();
d1.setDogName("小黑");
map.put("dog",d1);//d1->Dog->dog
return "result01";
}
Map是一个接口,Map中提供的方法比较少,尽量少用
- Model
@GetMapping("/data05")
public String m6(Model model){
System.out.println("实际的堆内置类型:"+model.getClass());
model.addAttribute("bookName","柳树model");
model.addAttribute("123",7777.7);
/**当存储的数据没有指定类,默认是其对应引用数据类型的首字母小写*/
model.addAttribute(666);//int->Integer->integer
model.addAttribute("断德");//String->string
Dog d1 = new Dog();
d1.setDogName("小黑");
model.addAttribute(d1);//d1->Dog->dog
return "result01";
}
}
Model是一个接口,其实现类为ExtendedModelMap,继承了ModelMap类。
源码:
public class ExtendedModelMap extends ModelMap implements Model
上述代码运行后发现控制台输出其实际类型:
- BindingAwareModelMap
@GetMapping("/data06")
public String m4(BindingAwareModelMap modelMap){
System.out.println("实际的堆内置类型:"+modelMap.getClass());
modelMap.addAttribute("bookName","柳树BindingAwareModelMap");
modelMap.addAttribute("123",9999.9);
/**当存储的数据没有指定类,默认是其对应引用数据类型的首字母小写*/
modelMap.addAttribute(666);//int->Integer->integer
modelMap.addAttribute("无始");//String->string
Dog d1 = new Dog();
d1.setDogName("小黑");
modelMap.addAttribute(d1);//d1->Dog->dog
return "result01";
}
源码:
public class BindingAwareModelMap extends ExtendedModelMap
public class ExtendedModelMap extends ModelMap implements Model
所以这三者不论使用哪一 个最后都是在隐含模型中的
6.2 重定向传递数据
new String("一叶遮天".getBytes("UTF-8"),"ISO8859-1"));
设置中文编码
@Controller
@RequestMapping("/redirect")
public class RedirectController {
@GetMapping("/data01")
public ModelAndView m1() throws UnsupportedEncodingException {
ModelAndView mav = new ModelAndView();
mav.setViewName("redirect:/view/result02.jsp");
mav.addObject("bookId",99);
mav.addObject("bookName",new String("一叶遮天".getBytes("UTF-8"),"ISO8859-1"));//设置中文编码
mav.addObject("hobby",new String[]{"100","200"});
return mav;
}
<body>
<h3>${param.bookId}</h3>
<h3>${param.bookName}</h3>
<h3>${paramValues.hobby[0]}</h3>
<h3>${paramValues.hobby[1]}</h3>
</body>
重定向执行结果:
http://127.0.0.1:8080/mvc/view/result02.jsp?bookId=99&bookName=一叶遮天hobby=100&hobby=200
:自动拼接
6.3 会话和应用范围传递数据
package com.framework.web;
@Controller
@RequestMapping("/server")
public class SessionApplicationController {
@Autowired
private HttpSession session;
@Autowired
private ServletContext application;
@GetMapping("/demo01")
@ResponseBody
public void m1(){//存储数据的过程
session.setAttribute("bookName","session-红楼梦");
session.setAttribute("author","session-曹雪芹");
application.setAttribute("bookName","application-西游记");
application.setAttribute("author","application-吴承恩");
}
@GetMapping("/demo02")
@ResponseBody
public void m2(){//移除数据
session.removeAttribute("bookName");
}
@GetMapping("/demo03")
@ResponseBody
public void m3(){//注销session
session.invalidate();
}
}
6.4 防止重复提交
对应放置表单的重复提交,我们可以使用JavaScript进行控制或者使用Token,SpringMVC提供了这种特殊场景的数据的传递方式
Controller->重定向->Controller->请求转发->JSP
在做一般的登录操作时候会用到这种方式
@Controller
@RequestMapping("/flash")
public class FlashValueController {
@GetMapping("/data01")
public String m1(RedirectAttributes ra){
ra.addFlashAttribute("message","您的账号或者密码错误,请重新登录");
return "redirect:/flash/data02";
}
@GetMapping("/data02")
public String m2(){
return "show";
}
}
七、数据绑定
在我们学习Java Web阶段通过HttpServletRequest
获取客户端数据的时候,获取的类型(String/String[]/Map<String,String[]>
)形式,Spring MVC框架内部提供了数据绑定的方式(绑定的过程中可以完成数据类型转换,提供大量内置数据类型转换器)
客户端通过路径或者表单传递数据->映射路径找到处理器Controller->传递参数->绑定进行处理(WebDataBinder)->在绑定的过程中完成数据的转换或者数据的验证
7.1 简单数据绑定
基本数据类型、包装类、字符串、数组、**日期(注意)**等类型
(1)HttpServletRequest
需要自己完成转换过程
在SpringBoot的控制器的方法中,如果return一个字符串,那么程序默认会跳转到这个字符串所表示的页面,而@ResponseBody注解的作用就是阻止这个功能的,这个注解可以让return的字符串直接返回到页面中或者直接返回到ajax的执行成功方法中。
@Controller
@RequestMapping("/simple")
public class SimpleDataController {
@Autowired
private HttpServletRequest request;
//<a href="simple/test00?id=90&money=80&hobby=basketball&hobby=足球">回顾:传统获取方式</a><br/>
@GetMapping("/test00")
@ResponseBody
public void m1(){
String idStr = request.getParameter("id");
int id = Integer.parseInt(idStr);//自己处理
System.out.println("id = " + id);
String moneyStr = request.getParameter("money");
double money = Double.parseDouble(moneyStr);
System.out.println("money = " + money);
String[] hobbies = request.getParameterValues("hobby");
System.out.println("Arrays.toString(hobbies) = " + Arrays.toString(hobbies));
}
}
${param.name} 等价于 request.getParamter(“name”),这两种方法一般用于服务器从页面或者客户端获取的内容,这些内容都是String的。
${requestScope.name} 等价于 request.getAttribute(“name”),一般是从服务器传递结果到页面,在页面中取出服务器端保存的值! ——来自
(2)@RequestParam
注解:功能强大
属性说明:
@RequestParam(name = "id",required = true) Integer userId
将客户端属性名称为"id"的值赋值给这个形参.
其中required
作用是必须传递
defaultValue
作用是赋值一个默认值
<a href="simple/test01?bookName=西游记&id=90&money=80&hobby=basketball&hobby=足球">@RequestParam的使用说明-正常测试</a><br/>
<a href="simple/test01?bookName=西游记&money=80&hobby=basketball&hobby=足球">@RequestParam的使用说明-400错误</a><br/>
<a href="simple/test01?id=90&hobby=basketball&hobby=足球">@RequestParam的使用说明-默认值测试</a><br/>
@InitBinder
只在@Controlle
中注解方法来为这个控制器注册一个绑定器初始化方法,方法只对本控制器有效。
初始方式
@GetMapping("/test01")
@ResponseBody
public void m1(
@RequestParam(name = "id",required = true) Integer userId,//String=>Integer
@RequestParam(name = "bookName",required = false) String book_name,//String=>String
@RequestParam(name="money",required = false,defaultValue = "123.456") Double bookMoney,//String=>Double
@RequestParam("hobby") String[] hobbyArray){//String=>String[] 简写方式
System.out.println("userId = " + userId);
System.out.println("book_name = " + book_name);
System.out.println("bookMoney = " + bookMoney);
System.out.println("hobbyArray = " + Arrays.toString(hobbyArray));
}
@InitBinder
public void initBinder(WebDataBinder binder){
System.out.println(Math.random()+".....绑定器,可以自定义");
}
就算是null也需要绑定转换
未传递id,则返回400错误
简写方式:推荐
当传递参数的名称和形参相同时,可省略 @RequestParam(name = "id",required = true)
这部分代码
设置只要数据合适,可以直接转换:Integer[] hobbyArray
缺点是不能设置默认值
<a href="simple/test02?bookName=西游记&id=90&money=80&hobby=999&hobby=777">简写方式=推荐=注意规则形参名称和传递名称一致</a><br/>
@GetMapping("/test02")
@ResponseBody
public void m2(/*@RequestParam(name = "id",required = false)*/ Integer id,
String bookName,Double money,Integer[] hobbyArray){
System.out.println("id = " + id);
System.out.println("bookName = " + bookName);
System.out.println("money = " + money);
System.out.println("hobbyArray = " + Arrays.toString(hobbyArray));
}
@InitBinder
public void initBinder(WebDataBinder binder){
System.out.println(Math.random()+".....绑定器,可以自定义");
}
绑定器绑定了四次
- 绑定Map类型()
<h3>Map类型</h3>
<a href="simple/test03?bookName=西游记&id=90&money=80">Map类型绑定,绑定的执行次数</a><br/>
@GetMapping("/test03")
@ResponseBody
public void m2(@RequestParam Map<String,Object> tempMap){
System.out.println("tempMap = " + tempMap);
}
@InitBinder
public void initBinder(WebDataBinder binder){
System.out.println(Math.random()+".....绑定器,可以自定义");
}
7.2 占位符数据绑定
占位符我们推荐传递简单的数据类型,占位符支持正则表达式
@PathVariable
是spring3.0的一个新功能:接收请求路径中占位符的值
@Controller
@RequestMapping("/path")
public class PathDataController {
@GetMapping("/test01/{id}/{name}")
@ResponseBody
public void m1(@PathVariable("id") Integer userId,@PathVariable/*("name")*/ String name){
System.out.println("userId = " + userId);
System.out.println("name = " + name);
}
@GetMapping("/test02/{id}/{name}")
@ResponseBody
public void m1(@PathVariable Map<String,Object> tempMap){
System.out.println("tempMap = " + tempMap);
}
@InitBinder
public void initBinder(WebDataBinder binder){
System.out.println(Math.random()+".....绑定器,可以自定义");
}
}
<h3>占位符传递数据</h3>
<a href="path/test01/100/悟空">占位符传递数据</a><br/>
<a href="path/test02/100/悟空">占位符传递数据-Map绑定</a><br/>
转换map类型不会经过绑定 只有类型对类型才会绑定
占位符绑定的时候不可省略@PathVariable
标签
7.3 自定义对象数据绑定
将客户端传递的数据直接转换成Java自定义对象,了解默认规则
(1)单个对象的绑定:
传递的名称为类中的属性名称,可以完成成String->对象的转换
当传递属性名称与类中不同时,输出结果为null
public class Student {
private String name;
private Integer age;
private Integer[] hobbies;
}
<form action="object/data01">
学生姓名:<input type="text" name="name"/><br/>
学生年龄:<input type="text" name="age"/><br/>
爱好:<!-- 经典的多选控件,复选框和多选列表 -->
<select name="hobbies" multiple>
<option value="100">篮球</option>
<option value="200">足球</option>
<option value="300">排球</option>
</select>
<br/>
<button>简单对象</button>
</form>
@Controller
@RequestMapping("/object")
public class ObjectBinderController {
@GetMapping("/data01")
@ResponseBody
public Student m1(int age, Student student){
System.out.println("age = " + age);
return student;
}
@InitBinder
public void initBinder(WebDataBinder binder){
System.out.println(Math.random()+".....绑定器,可以自定义");
}
}
(2)关联对象:不同的属性
1. 关联对象classInfo:(不同属性)
public class ClassInfo {
private String className;
private Integer classNumber;
}
- xml文件
<form action="object/data02">
学生姓名:<input type="text" name="name"/><br/>
学生年龄:<input type="text" name="age"/><br/>
爱好:<!-- 经典的多选控件,复选框和多选列表 -->
<select name="hobbies" multiple>
<option value="100">篮球</option>
<option value="200">足球</option>
<option value="300">排球</option>
</select><br/>
<!-- Student中关联对象的属性建立关联关系 -->
班级名称:<input type="text" name="className"><br/>
班级人数:<input type="text" name="classNumber"><br/>
<br/>
<button>关联对象和同属性对象区分</button>
- 测试文件
@GetMapping("/data02")
@ResponseBody
public Student m1(Student student , ClassInfo classInfo){
//手动建立关联
student.setClassInfo(classInfo);
return student;
}
2. 关联对象group:(相同属性:使用类中的属性进行区分,并且直接建立关联关系)
public class Group {
private String name;
private Integer age;
}
- student对象:
public class Student {
private String name;
private Integer age;
private Integer[] hobbies;
//关联对象-不同属性
private ClassInfo classInfo;
//关联对象-相同属性
private Group group;
}
- group对象:
public class Group {
private String name;
private Integer age;
如果直接小组名称:<input type="text" name="name"/><br/>
这样写,返回的student中学生名字会有两个值,group名字同样有两个
属性名称相同时,在xml配置中标明类名前缀:如
name="group.name"
测试文件就不用手动建立关联
- xml文件:
<form action="object/data03">
学生姓名:<input type="text" name="name"/><br/>
学生年龄:<input type="text" name="age"/><br/>
爱好:<!-- 经典的多选控件,复选框和多选列表 -->
<select name="hobbies" multiple>
<option value="100">篮球</option>
<option value="200">足球</option>
<option value="300">排球</option>
</select><br/>
<!-- Student中关联对象的属性建立关联关系 -->
班级名称:<input type="text" name="classInfo.className"><br/>
班级人数:<input type="text" name="classInfo.classNumber"><br/>
小组名称:<input type="text" name="group.name"/><br/>
小组年龄:<input type="text" name="group.age"/><br/>
<br/>
<button>关联对象和同属性对象区分</button>
- 测试文件
@GetMapping("/data03")
@ResponseBody
public Student m1(Student student , ClassInfo classInfo, Group group){
return student;
}
@InitBinder
public void initBinder(WebDataBinder binder){
System.out.println(Math.random()+".....绑定器,可以自定义");
}
3. 同属性问题(推荐):可以通过第三方建立关联对象属性进行区分
<form action="object/data04">
学生姓名:<input type="text" name="student.name"/><br/>
学生年龄:<input type="text" name="student.age"/><br/>
爱好:<!-- 经典的多选控件,复选框和多选列表 -->
<select name="student.hobbies" multiple>
<option value="100">篮球</option>
<option value="200">足球</option>
<option value="300">排球</option>
</select><br/>
老师姓名:<input type="text" name="teacher.name"/><br/>
老师年龄:<input type="text" name="teacher.age"/><br/>
爱好:<!-- 经典的多选控件,复选框和多选列表 -->
<select name="teacher.hobbies" multiple>
<option value="100">剑道</option>
<option value="200">养雕</option>
<option value="300">高人寂寞</option>
</select><br/>
<br/>
<button>同属性对象推荐方式</button>
</form>
</form>
public class MyModel {
private Teacher teacher;
private Student student;
}
@GetMapping("/data04")
@ResponseBody
public MyModel m3(MyModel myModel){
return myModel;
}
@InitBinder
public void initBinder(WebDataBinder binder){
System.out.println(Math.random()+".....绑定器,可以自定义");
}
7.4 Map类型的数据绑定
通过
返回对象名称[自定义属性]
来完成
如学生年龄:<input type="text" name="queryMap[studentAge]"/><br/>
自定义返回对象的属性值:
<form action="object/data05">
学生姓名:<input type="text" name="queryMap[studentName]"/><br/>
学生年龄:<input type="text" name="queryMap[studentAge]"/><br/>
爱好:<!-- 经典的多选控件,复选框和多选列表 -->
<select name="queryMap[studentHobbyArray]" multiple>
<option value="100">篮球</option>
<option value="200">足球</option>
<option value="300">排球</option>
</select><br/>
老师姓名:<input type="text" name="queryMap[teacherName]"/><br/>
老师年龄:<input type="text" name="queryMap[teacherAge]"/><br/>
爱好:<!-- 经典的多选控件,复选框和多选列表 -->
<select name="queryMap[teacherHobbyArray]" multiple>
<option value="100">剑道</option>
<option value="200">养雕</option>
<option value="300">高人寂寞</option>
</select><br/>
<br/>
<button>关联对象和同属性对象区分</button>
</form>
- 第三放对象文件
public class MyModel {
private Student student;
private Teacher teacher;
private Map<String,Object> queryMap;
}
- 测试文件
@GetMapping("/data05")
@ResponseBody
public MyModel m2(MyModel myModel){
return myModel;
}
八、文件上传和下载
文件上传也是数据(二进制流的方式multipart/form-data
),需要将该内容类型处理成Java对象(commons-fileupload.jar
或者Servlet 3.0
),Spring MVC已经封装了这两种方式(一种二进制流的解析器,当发现是multipart/form-data
,看你默认启动那种解析器的实现方式处理)
8.1 文件上传的准备
(1)客户端文件上传的约定
- 必须使用
post
请求,不支持(PUT
/DELETE
)- 可以使用表单
- 可以使用Ajax
- 传递的文件类型,必须设置为
multipart/form-data
内容类型 - 文件域(单个或者多个
multiple
属性)
(2)服务端文件上传的约定
commons-fileupload.jar
工具类的支持- 配置解析器处理
multipart/form-data
- 处理成
MultipartFile
或者MultipartFile[]
8.2 文件上传的步骤
(1)新建表单
<form action="attr01", method="post", enctype="multipart/form-data">
普通控件<input type="text" name="userName"><br/>
文件1:<input type="text" name="myFile"><br/>
<button>文件上传</button>
</form>
(2)配置处理multipart/form-data
解析器
前端控制器给解析器设置了默认名称
public class DispatcherServlet extends FrameworkServlet {
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
}
使用解析器id名称只能叫multipartResolver
<!-- 文件上传的解析器,固定名称 id="multipartResolver" -->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
<!-- 设置上传的编码格式 -->
<property name="defaultEncoding" value="UTF-8"/>
<!-- 上传所有文件总大小 -->
<property name="maxUploadSize" value="#{1024*1024*50}"/>
<!-- 单个文件大小 -->
<property name="maxUploadSizePerFile" value="#{1024*1024*5}"/>
<!-- 设置缓存 -->
<property name="maxInMemorySize" value="2048"/>
<!-- 延迟上传,提供上传效率 -->
<property name="resolveLazily" value="true"/>
<!-- 临时上传的目录 -->
<property name="uploadTempDir" value="D:/Temp"/>
</bean>
(3)控制器数据绑定
@Controller
public class uploadFileController {
@PostMapping("/attr01")
@ResponseBody
public void upload01(String userName, MultipartFile myFile){
System.out.println("堆中实际的类型:"+myFile.getClass().getName());
System.out.println("普通控件 = " + userName);
System.out.println("文件名称:"+myFile.getOriginalFilename());
System.out.println("文件大小:"+myFile.getSize()+"B(字节)");
System.out.println("文件类型(MIME):"+myFile.getContentType());
System.out.println("控件名称:"+myFile.getName());
//System.out.println("输入流:"+myFile.getInputStream());
//System.out.println("字节数组:"+myFile.getBytes());
}
(4)文件上传的方式
两种方式
一是输入输出流
二是springmvc封装的方法transferTo()
<form action="attr02" method="post" enctype="multipart/form-data">
普通的控件:<input type="text" name="userName"><br/>
文件1:<input type="file" name="myFile"><br/>
<button>文件上传</button>
</form>
<form action="attr03" method="post" enctype="multipart/form-data">
普通的控件:<input type="text" name="userName"><br/>
文件1:<input type="file" name="myFile"><br/>
<button>文件上传</button>
</form>
//输入输出流上传
@PostMapping("/attr02")
@ResponseBody
public void upload02(String userName, MultipartFile myFile) throws IOException {
InputStream in = myFile.getInputStream();
OutputStream out = new FileOutputStream("D:/upload/"+myFile.getOriginalFilename());
IOUtils.copy(in,out);
}
@PostMapping("/attr03")
@ResponseBody
public void upload03(String userName, MultipartFile myFile) throws IOException {
myFile.transferTo(new File("D:/upload/"+myFile.getOriginalFilename()));
}
8.3 文件上传“套路”
前文写过
@Autowired
private ServletContext appliaction;
@PostMapping("/attr04")
@ResponseBody
public void upload04(String userName, MultipartFile myFile) throws IOException {
//1.获取服务器上传文件的所在目录
String path = appliaction.getRealPath("/attr/");
//2.跟磁盘建立联系
File folder = new File(path);
if(!folder.exists()){//判断目录是否存在
folder.mkdirs();//创建文件夹
}
//3.获取上传文件的名称
String fileName = myFile.getOriginalFilename();
//4.获取上传文件名称的后缀名称
String ext = FilenameUtils.getExtension(fileName);
//5.创建新的文件名称(唯一)
String newFileName = getUniqueFileName()+"."+ext;
//6.文件上传(不做验证,因为在前端可以设置验证)
myFile.transferTo(new File(path+"/"+newFileName));
}
public static String getUniqueFileName(){
return UUID.randomUUID().toString().replaceAll("-","");
}
8.4 文件上传和下载
目的:知道文件下载的3种方式,引出静态资源的访问问题。
(1)文件上传保存数据(模拟操作)
@PostMapping("/attr05")
public String upload04(String userName, MultipartFile myFile, Model model) throws IOException {
//1.获取服务器上传文件的所在目录
String path = appliaction.getRealPath("/attr/");
//2.跟磁盘建立联系
File folder = new File(path);
if(!folder.exists()){//判断目录是否存在
folder.mkdirs();//创建文件夹
}
//3.获取上传文件的名称
String fileName = myFile.getOriginalFilename();
model.addAttribute("showName", fileName);//元素内容
//4.获取上传文件名称的后缀名称
String ext = FilenameUtils.getExtension(fileName);
//5.创建新的文件名称(唯一)
String newFileName = getUniqueFileName()+"."+ext;
model.addAttribute("serverFileName",newFileName);//添加到href中
//6.文件上传(不做验证,因为在前端可以设置验证)
myFile.transferTo(new File(path+"/"+newFileName));
return "download";
}
(2)下载页面
<body>
<h3>1.简单的下载,直接访问资源即可(若浏览器支持则直接打开,不支持则下载)</h3>
<a href="attr/${requestScope.serverName} ">${showName}</a>
<h3>2.H5中的download属性强制下载</h3>
<a href="attr/${requestScope.serverName}" download="${requestScope.showName}">${showName}</a>
<h3>3.二进制流的方式下载</h3>>
<a href="download?show=${showName}&real=${serverName}">${showName}_download</a>
</body>
只有第三种可正常下载,前两者报错:
No mapping for GET /mvc/attr/0ae503efb5fb4e42bf9c6e81614dfe83.md
原因:
所有的请求都被前端控制器DispatcherServlet处理,通过URL映射找到处理Controller,请求找不到NO Mapping
如何解决:
静态资源无法被直接访问,那么我们需要做到当访问的是静态资源的时候,让DispatcherServlet对其不处理
流的方式下载
@GetMapping("/download")
public ResponseEntity<byte[]> downloadFile(String show, String real) throws Exception {
String path = appliaction.getRealPath("/attr/");
File file = new File(path+"/"+real);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
show = new String(show.getBytes("UTF-8"),"ISO8859-1");
httpHeaders.setContentDispositionFormData("attachment",show);
return new ResponseEntity<>(FileUtils.readFileToByteArray(file),httpHeaders, HttpStatus.CREATED);
}
九、静态资源访问
(1)使用服务器默认的排除方案
当我们启动服务器的时候,会优先实例化WEB服务器提供的默认的Servlet(
default
),在该Servlet中有对静态资源的处理
如tomcat中web.xml文件里关于注册的servlet代码:
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
配置方案如下:
<!-- 方式一:排除静态资源,需要我们自己设置完整路径 -->
<mvc:default-servlet-handler default-servlet-name="default"/>
现在文件下载的demo中,三种下载方式皆可正常下载
(2)建立静态资源的映射路径
mvc:resources 参数说明:
<mvc:resources mapping="/映射的路径/**" location="本地静态资源的位置"/>
=>location+**=真实的资源位置
<!--方式二:静态资源映射路径-->
<mvc:resources mapping="/love/**" location="/attr/"/>
<mvc:resources mapping="/abcd/**" location="/attr/"/>
<mvc:resources mapping="/kkk/**" location="/attr/"/>
<h3>4.映射路径</h3>
<a href="love/${requestScope.serverName} ">${showName}</a>
<a href="abcd/${requestScope.serverName} ">${showName}</a>
<a href="kkk/${requestScope.serverName} ">${showName}</a>
如若这样配置src="images/a/b/c/d.jpg"
那么真实的位置 为/attr/a/b/c/d.jpg
最后
以上就是风中衬衫为你收集整理的SpringMVC框架学习笔记(三)(数据传递,数据绑定,文件上传与下载)的全部内容,希望文章能够帮你解决SpringMVC框架学习笔记(三)(数据传递,数据绑定,文件上传与下载)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复