24.12.27 SpringMVCDay02
-
- enctype必须是 multipart/form-data
<form action="/mvc/book/doUpload1" method="post" enctype="multipart/form-data">
<p>
<input type="file" name="img_url">
</p>
<p>
<input type="submit" value="上传">
</p>
</form>
@Autowired
HttpServletRequest request;
@PostMapping("/doUpload1")
@ResponseBody
public String doUpload1() throws ServletException, IOException {
//获取Request对象
//getPart(页面表单提交中,获取name=img_url的值
Part part = request.getPart("img_url");
//文件存哪里
//D:\webFolder\mvcdemo1\target\mvcdemo\
String realPath = request.getServletContext().getRealPath("/");
//D:\webFolder\mvcdemo1\target\mvcdemo\/imgs/
String basePath = realPath + "/imgs/";
System.out.println(basePath);
//文件夹是否存在
File baseFile = new File(basePath);
if (!baseFile.exists()){
baseFile.mkdirs();
}
//文件名
String fileName = part.getSubmittedFileName();
part.write(basePath + fileName);
return "success";
}
多文件
multiple属性
<form action="/mvc/book/doUpload2" method="post" enctype="multipart/form-data">
<p>
<input type="file" name="img_url" multiple>
</p>
<p>
<input type="submit" value="上传">
</p>
</form>
@PostMapping("/doUpload2")
@ResponseBody
public String doUpload2() throws ServletException, IOException {
//获取 所有的文件
Collection<Part> parts = request.getParts();
String realPath = request.getServletContext().getRealPath("/imgs/");
System.out.println(realPath);
File realFile = new File(realPath);
if (!realFile.exists()){
realFile.mkdirs();
}
parts.forEach(part -> {
String fileName = part.getSubmittedFileName();
try {
part.write(realPath + fileName);
} catch (IOException e) {
//不处理异常,仅仅是打印看看
e.printStackTrace();
}
});
return "success";
}
MVC方式上传文件
- 引入依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
- mvc.xml
<!--上传文件的配置-->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"/>
</bean>
单文件
//img_url表单提交中的name属性
@PostMapping("/doUpload3")
@ResponseBody
public String doUpload3(MultipartFile img_url) throws IOException {
//文件名
String fileName = img_url.getOriginalFilename();
//获取保存路径
String realPath = request.getServletContext().getRealPath("/imgs/");
System.out.println(realPath);
File realFile = new File(realPath);
if (!realFile.exists()){
realFile.mkdirs();
}
//文件对象.transferTo(存储的位置+文件名)
img_url.transferTo(new File(realPath + fileName));
return "success";
}
多文件
@PostMapping("/doUpload4")
@ResponseBody
public String doUpload4(MultipartFile[] img_url) throws IOException {
if (img_url.length > 0){
for (MultipartFile file : img_url){
file.transferTo(new File(getFileName(file)));
}
}
return "success";
}
private String getFileName(MultipartFile file){
//文件名
String fileName = file.getOriginalFilename();
//获取保存路径
String realPath = request.getServletContext().getRealPath("/imgs/");
File realFile = new File(realPath);
if (!realFile.exists()){
realFile.mkdirs();
}
return realPath + fileName;
}
文件下载
默认情况下,mvc框架,无法识别静态资源文件的
超链接下载
<ul>
<li>
<a href="/mvc/save/test1.docx">word文档</a>
</li>
<li>
<a href="/mvc/save/test2.jpg">图片</a>
</li>
<li>
<a href="/mvc/save/test3.pdf">PDF</a>
</li>
<li>
<a href="/mvc/save/test4.rar">压缩文件</a>
</li>
<li>
<a href="/mvc/save/test5.xlsx">Excel</a>
</li>
</ul>
如果这个文件,浏览支持,可以打开,就会直接在浏览器中打开,不进行下载
如果这个文件,浏览器无法打开,跳出下载页面
好处:简单,易学.写一个url地址超链接即可
坏处:仅能让部分格式的文件下载
流下载
这个下载 ,要经过服务器,让服务器写出文件流 给浏览器,触发下载
- ResponseEntity
@Autowired
HttpServletRequest request;
@GetMapping("/test1")
public ResponseEntity<byte[]> download(String name) throws IOException {
//获取文件路径
String realPath = request.getServletContext().getRealPath("/save/");
//文件的全路径
String allPath = realPath + name;
//获取下载需要的文件流信息
FileInputStream inputStream = new FileInputStream(allPath);
//获取流的数组
byte[] buffer = new byte[inputStream.available()];
//读取
inputStream.read(buffer);
//文件下载的时候,显示的名字,需要改文件名了,再修改
//设置响应的头信息,告诉浏览器,返回的是文件
HttpHeaders httpHeaders = new HttpHeaders();
//设置响应的头信息,访问文件名中文乱码
httpHeaders.setContentDispositionFormData("attachment", URLEncoder.encode(name,"utf-8"));
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(buffer,httpHeaders, HttpStatus.OK);
return responseEntity;
}
- HttpServletResponse
@Autowired
HttpServletResponse response;
@GetMapping("/test2")
public void download2(String name) throws FileNotFoundException, UnsupportedEncodingException {
//获取文件路径
String realPath = request.getServletContext().getRealPath("/save/");
//文件的全路径
String allPath = realPath + name;
//判断文件是否存在
File file = new File(allPath);
if (!file.exists()){
throw new FileNotFoundException(name+"文件不存在");
}
//代码能执行到这一行,说明文件肯定存在
//编码格式是utf-8的文件名
String encodeFileName = URLEncoder.encode(name, StandardCharsets.UTF_8.toString());
//设置头信息 header
response.setHeader("Content-Disposition","attachment;filename*=UTF-8''"+encodeFileName);
//使用 try--with --resources 关闭流
try(FileInputStream inputStream = new FileInputStream(file);
OutputStream outputStream = response.getOutputStream()){
IOUtils.copy(inputStream,outputStream);
outputStream.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
异常处理
- 异常页面
-
- 修改web.xml (会直接跳到这个页面)
<error-page>
<location>/error.jsp</location>
</error-page>
- 异常类
@Component
public class TestController implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object o,
Exception e) {
//产生异常的时候,调用这个方法
System.out.println(e.getMessage());
e.printStackTrace();
//配置返回值
ModelAndView mv = new ModelAndView();
mv.setViewName("game/success");//会直接跳到 webapp下的这个页面
return mv;
}
}
- @RestControllerAdvice
用来处理指定异常(自定义异常类)
@RestControllerAdvice
public class JavasmExceptionAdvice {
//当ArithmeticException被抛出来的时候,执行当前方法
@ExceptionHandler(ArithmeticException.class)
public String f1(ArithmeticException e){
System.out.println("算术异常");
e.printStackTrace();
//返回值,会代替原有方法的返回内容
return e.getMessage();
}
//当抛出JavasmException的时候,执行当前方法
@ExceptionHandler(JavasmException.class)
public Map<String,Object> f2(JavasmException e){
Map<String,Object> map = new HashMap<>();
map.put("code",500);
map.put("msg","自定义异常"+e.getMessage());
return map;
}
}
public class JavasmException extends RuntimeException{
public JavasmException() {
}
public JavasmException(String message) {
super(message);
}
}
拦截器
拦截器 和 过滤器 的区别是什么?
过滤器:web容器,不属于框架,请求进入tomcat之后,先经过的就是过滤器
拦截器:属于spring容器,springweb中,属于 容器的servlet,相当于,Servlet内部的请求拦截
public class TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("pre--xxx之前----在Controller的方法 执行之前");
//返回的数据是Boolean类型,返回true,允许进入下一层(可能是下一个拦截器,也可能是Controller)
//返回false????
//response.getWriter().write("xxxx");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("post--xxx之后-----被访问的方法 执行结束之后,执行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("页面渲染成功之后执行");
}
}
案例登录拦截
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//只有登录的用户才能访问系统
HttpSession session = request.getSession();
Object user = session.getAttribute("user");//登录的数据存在session,在这里取出
if (user != null){
//已经登录了
return true;
}
Map<String,Object> map = new HashMap<>();
map.put("code",2001);
map.put("msg","未登录");
String s = JSON.toJSONString(map);
response.setContentType("application/json;charset=utf-8");//预防乱码
PrintWriter writer = response.getWriter();
writer.write(s);
writer.flush();
writer.close();
return false;
}
}
<?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">
<!-- 开启包扫描-->
<context:component-scan base-package="com.javasm"/>
<!--代替适配器等配置-->
<mvc:annotation-driven/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--跳转的jsp页面,统一添加前缀-->
<property name="prefix" value="/WEB-INF/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<!--静态资源文件放过-->
<mvc:default-servlet-handler/>
<!--上传文件的配置-->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"/>
</bean>
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--被拦截的路径-->
<!--所有以/game开头的请求,都要经过拦截器-->
<mvc:mapping path="/game/**"/>
<mvc:mapping path="/player/**"/>
<!--在包含的路径中,排除其中的某些路径-->
<mvc:exclude-mapping path="/game/test1"/>
<!--拦截器类-->
<bean class="com.javasm.interceptor.TestInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/> //拦截页面的范围
<mvc:exclude-mapping path="/game/test1"/> //不会被拦截的页面
<mvc:exclude-mapping path="/login/**"/>
<bean class="com.javasm.interceptor.LoginInterceptor"/> //拦截所在的位置
</mvc:interceptor>
</mvc:interceptors>
</beans>
数据类型转换
Field error in object 'bookModel' on field 'ctime': rejected value [2024-12-27]; codes [typeMismatch.bookModel.ctime,typeMismatch.ctime,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [bookModel.ctime,ctime]; arguments []; default message [ctime]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'ctime'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Date] for value '2024-12-27'; nested exception is java.lang.IllegalArgumentException]]
表单提交的是String类型数据,尝试自动转成Date类型,失败了
@PostMapping("/doAdd2")
@ResponseBody
public Date doAdd2(String name,@DateTimeFormat(pattern = "yyyy-MM-dd") Date ctime){
return ctime;
}
@PostMapping("/doAdd")
@ResponseBody
public BookModel doAdd(BookModel book){
return book;
}
@Data
public class BookModel {
private Integer id;
private String name;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date ctime;
}
自定义参数类型转换器
SimpleDate转换
public class StringToDate implements Converter<String, Date> {
@Override
public Date convert(String s) {
//传入的参数,等待被转换的2024-12-27 字符串
System.out.println(s);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = simpleDateFormat.parse(s);
} catch (ParseException e) {
throw new RuntimeException(e);
}
return date;
}
}
<?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">
<!-- 开启包扫描-->
<context:component-scan base-package="com.javasm"/>
<!--代替适配器等配置-->
<mvc:annotation-driven conversion-service="conversionJavasmService"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--跳转的jsp页面,统一添加前缀-->
<property name="prefix" value="/WEB-INF/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<!--静态资源文件放过-->
<mvc:default-servlet-handler/>
<!--上传文件的配置-->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"/>
</bean>
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--被拦截的路径-->
<!--所有以/game开头的请求,都要经过拦截器-->
<mvc:mapping path="/game/**"/>
<mvc:mapping path="/player/**"/>
<!--在包含的路径中,排除其中的某些路径-->
<mvc:exclude-mapping path="/game/test1"/>
<!--拦截器类-->
<bean class="com.javasm.interceptor.TestInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/game/test1"/>
<mvc:exclude-mapping path="/login/**"/>
<bean class="com.javasm.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<!--消息类型转换-->
<bean id="conversionJavasmService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.javasm.convert.StringToDate"/>
</set>
</property>
</bean>
</beans>
localdateTime
Field error in object 'bookModel' on field 'utime': rejected value [2024-12-27T16:08]; codes [typeMismatch.bookModel.utime,typeMismatch.utime,typeMismatch.java.time.LocalDateTime,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [bookModel.utime,utime]; arguments []; default message [utime]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDateTime' for property 'utime'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDateTime] for value '2024-12-27T16:08'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2024-12-27T16:08]]]
public class StringToDateTime implements Converter<String, LocalDateTime> {
@Override
public LocalDateTime convert(String s) {
return LocalDateTime.parse(s, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
}
<!--消息类型转换-->
<bean id="conversionJavasmService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.javasm.convert.StringToDate"/>
<bean class="com.javasm.convert.StringToDateTime"/>
</set>
</property>
</bean>
JSON数据(用JSON传输数据)
- 引入依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.11.2</version>
</dependency>
- 加注解
@GetMapping ("/doAdd3")
@ResponseBody
public BookModel doAdd3(@RequestBody BookModel book){
System.out.println(book);
return book;
}
@Data
public class BookModel {
private Integer id;
private String name;
//@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date ctime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime utime;
}
{
"id":1001,
"name":"<刑法>",
"ctime":"2024-12-27",
"utime":"2024-12-27 16:09:30"
}
json时间,可以写2024-01-27 09:09:09
不能写 2024-1-27 9:9:9