multipart/form-data
(没有这个属性值的话, 文件的内容是提交不过去的) 要使用request.getInputStream()来获取数据.
注意:
我们做文件上传一般会借助第三方组件(jar, 框架 SpringMVC)实现文件上传.
serlvet3.0
commons-fileupload : apache出品的一款专门处理文件上传的工具包
struts2(底层封装了:commons-fileupload)
SpringMVC(底层封装了:commons-fileupload)
步骤:
实现:
创建Maven工程,添加依赖
commons-fileupload
commons-fileupload
1.3.1
创建前端index.jsp页面
传统文件上传
创建FileUploadController控制器
@Controller
public class FileUploadController {
@RequestMapping("/upload")
public String upload(HttpServletRequest req, MultipartFile uploadFile, String desc) throws IOException {
System.out.println("desc=" + desc);
// 文件名
String filename = uploadFile.getOriginalFilename();
// 生成唯一文件名
String uniqueName = UploadUtils.getUUIDName(filename);
// 获取保存文件的路径
String baseDir = req.getSession().getServletContext().getRealPath("/upload");
// /upload/2/A
// 产生2层目录结构, UploadUtils这个类在资料中有
String dir = UploadUtils.getDir();
// 创建保存的目录
File saveDir = new File(baseDir, dir);
if(!saveDir.exists()){
saveDir.mkdirs();// 创建多层结构
}
// 保存文件
uploadFile.transferTo(new File(saveDir, uniqueName));
return "success";
}
}
在springmvc.xml配置文件解析器
注意:文件上传的解析器 id 是固定的,不能起别的名称,否则无法实现请求参数的绑定。(不光是文件,其他
字段也将无法绑定)
在实际开发中,我们会有很多处理不同功能的服务器(注意:此处说的不是服务器集群)。 例如:
应用服务器:负责部署我们的应用
数据库服务器:运行我们的数据库
缓存和消息服务器:负责处理大并发访问的缓存和消息
文件服务器:负责存储用户上传文件的服务器。
分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。
准备两个服务器, 文件服务的要修改tomcat的的conf目录下的web.xml, 添加readonly参数为false
在springmvc fileupload工程
<dependency>
<groupId>com.sun.jerseygroupId>
<artifactId>jersey-coreartifactId>
<version>1.18.1version>
dependency>
<dependency>
<groupId>com.sun.jerseygroupId>
<artifactId>jersey-clientartifactId>
<version>1.18.1version>
dependency>
上传到文件服务器
<form action="/upload2" enctype="multipart/form-data" method="post">
<input type="file" name="uploadFile"><br/>
<input name="desc"><br/>
<input type="submit" value="提交">
form>
/**
* 把上传的文件上传到文件服务器
* @param req
* @param uploadFile
* @param desc
* @return
* @throws IOException
*/
@RequestMapping("/upload2")
public String upload2(HttpServletRequest req, MultipartFile uploadFile, String desc) throws IOException {
System.out.println("desc=" + desc);
// 文件名
String filename = uploadFile.getOriginalFilename();
// 生成唯一文件名
String uniqueName = UploadUtils.getUUIDName(filename);
// 保存到文件服务器
// 文件服务器的连接
String url = "http://localhost:9090/upload/";
// jersey客户諯
Client client = Client.create();
// 定位存储的目录, 服务器的地址
// http://localhost:9090/upload/asadfasdfasf.jpg
WebResource webResource = client.resource(url + uniqueName);
// 上传
webResource.put(uploadFile.getBytes());
return "success";
}
为什么要分服务器存储?
系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:
springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。
步骤:
目的: 统一的管理异常, 方便统一管理错误提示语
/**
* 自定义异常类
*/
public class SysException extends RuntimeException {
public SysException(String msg){
super(msg);
}
}
/**
* Description: 全局异常处理
*/
public class SysExceptionResolver implements HandlerExceptionResolver {
/**
* 处理异常
* @param request
* @param response
* @param handler
* @param ex controller往上抛的异常
* @return
*/
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// 返回值有哪些?
// jsp, request转发, response 重定向, reponse.write 返回json数据
ModelAndView mv = new ModelAndView();
// 对已知道的异常,只要提示信息
String message = null;
if (ex instanceof SysException){
SysException sysException = (SysException) ex;
// 自己报错的信息
message = ex.getMessage();
}else{
// 对未知的异常,包装下再返回给页面
message = "发生未知异常,请联系管理员";
}
// 返回给前端的提示信息
mv.addObject("message",message);
mv.setViewName("error");
ex.printStackTrace();// 工作中,不能直接调用这句话.
return mv;
}
}
<bean id="sysExceptionResolver" class="com.itheima.exception.SysExceptionResolver"/>
Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器(自己编写的Controller)进行预处理和后处理。用户可以自己定义一些拦截器来实现特定的功能。谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。说到这里,可能大家脑海中有了一个疑问,这不是我们之前学的过滤器吗?是的它和过滤器是有几分相似,但
是也有区别,接下来我们就来说说他们的区别:
编写个一测试用的ControllerDemo
@Controller
public class ControllerDemo {
@RequestMapping("/demo01")
public String demo01(){
System.out.println("ControllerDemo.demo01执行了......");
return "success";
}
}
public class HandlerInterceptorDemo1 implements HandlerInterceptor {
/**
* 前置拦截, 在调用controller的方法前执行
* @param request
* @param response
* @param handler
* @return true: 放行, false:阻止
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// response 可以重定向, 用户登陆认证(session中不存存)
System.out.println("HandlerInterceptorDemo1.preHandle 执行了");
return true;
}
}
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="intercepter01" class="com.itheima.interceptor.HandlerInterceptorDemo1">bean>
mvc:interceptor>
mvc:interceptors>
拦截器的处理结果,莫过于两种:
放行: 如果后面还有拦截器就执行下一个拦截器,如果后面没有了拦截器,就执行Controller方法
拦截: 但是注意,拦截后也需要返回到一个具体的结果(页面,Controller)。
public class Intercepter01 implements HandlerInterceptor {
@Override
//在达到目标方法之前执行(拦截的方法)
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle 执行了...");
//转发到拦截后的页面
//request.getRequestDispatcher("/intercepter01.jsp").forward(request,response);
//转发到controller
request.getRequestDispatcher("/demo01/fun02").forward(request,response);
return false;//返回true放行, 返回false拦截
}
public class Intercepter01 implements HandlerInterceptor {
@Override
//在达到目标方法之前执行(拦截的方法)
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle 执行了...");
return true;//返回true放行, 返回false拦截
}
@Override
//在目标方法执行完成之后,完成页面渲染之前执行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
System.out.println("postHandle 执行了...");
}
@Override
//完成页面渲染之后执行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
System.out.println("afterCompletion 执行了...");
}
}
我们可以配置多个拦截器, 所以就存在一个优先级问题了.多个拦截器的优先级是按照配置的顺序决定的。
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/demo01/fun02"/>
<bean id="intercepter01" class="com.itheima.web.interceptor.Intercepter01">bean>
mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/demo01/fun02"/>
<bean id="intercepter02" class="com.itheima.web.interceptor.Intercepter02">bean>
mvc:interceptor>
mvc:interceptors>
拦截器是一个链式模式
只有preHandler方法才有返回true 放行,false拦截
拦截器的跳转 return false, request转发,response重定向
preHandle->postHandle->afterCompletion
多个执行的顺序按配置文件配置的顺序,从上而下执行。多个时,先进(preHandle)后出(postHandle,afterCompletion)