<form action="test/fileupload" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload" /><br/>
<input type="submit" value="上传" />
</form>
当form表单的enctype取值不是默认值后,request.getParameter()将失效。
enctype=”application/x-www-form-urlencoded”时,form表单的正文内容是: key=value&key=value&key=value
当form表单的enctype取值为mutilpart/form-data时,请求正文内容就变成: 每一部分都是MIME类型描述的正文,以此来实现文件上传
通过流的方式上传文件
@Controller
@RequestMapping("test")
public class test {
@RequestMapping("upload1")
//@RequestParam("file1") 将name=file1控件得到的文件封装成CommonsMultipartFile对象
public String testUpload1(@RequestParam("file1")CommonsMultipartFile file){
System.out.println("通过流的方式上传文件.....");
//用来检测程序运行时间
long startTime=System.currentTimeMillis();
//获取上传的文件名
System.out.println("fileName:"+file.getOriginalFilename());
try {
//获取输出流(上传的目录和新文件名,新文件名为上传的时间戳+原文件名)
OutputStream os=new FileOutputStream("E:/"+new Date().getTime()+file.getOriginalFilename());
//获取输入流 CommonsMultipartFile 中可以直接得到文件的流
InputStream is=file.getInputStream();
int temp;
//一个一个字节的读取并写入
while((temp=is.read())!=(-1)){
os.write(temp);
}
os.flush();
os.close();
is.close();
} catch (Exception e) {
e.printStackTrace();
}
long endTime=System.currentTimeMillis();
System.out.println("通过流的方式上传文件的运行时间:"+String.valueOf(endTime-startTime)+"ms");
return "success";
}
}
@RequestMapping("upload2")
public String testUpload2(@RequestParam("file2") CommonsMultipartFile file) throws IOException {
System.out.println("采用file.transferTo()来保存上传的文件");
long startTime=System.currentTimeMillis();
System.out.println("fileName:"+file.getOriginalFilename());
String path="E:/"+new Date().getTime()+file.getOriginalFilename();
File newFile=new File(path);
//通过CommonsMultipartFile的transferTo方法直接写文件(注意这个时候)
file.transferTo(newFile);
long endTime=System.currentTimeMillis();
System.out.println("file.transferTo()上传文件的运行时间:"+String.valueOf(endTime-startTime)+"ms");
return "success";
}
前提:在springMVC的配置文件中配置文件解析器
<!--配置文件解析器对象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--配置上传文件的最大大小-->
<property name="maxUploadSize" value="10485760" />
<property name="maxInMemorySize" value="4096" />
<property name="defaultEncoding" value="UTF-8"></property>
</bean>
@RequestMapping("upload3")
//参数中的file3必须和前端上传文件的控件name=file3属性值保持一致
public String testUpload3(MultipartFile file3) throws IOException {
System.out.println("采用SpringMVC提供的上传文件方法");
long startTime=System.currentTimeMillis();
String filename = file3.getOriginalFilename();
System.out.println("fileName:"+filename);
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid+"_"+filename;//新文件名=UUID+原文件名
String path="E:/";
file3.transferTo(new File(path,filename));//文件传输的路径和文件名
long endTime=System.currentTimeMillis();
System.out.println("SpringMVC提供的上传文件方法的运行时间:"+String.valueOf(endTime-startTime)+"ms");
return "success";
}
运行结果(上传同一个文件 154kb):
通过流的方式上传文件的运行时间:614ms
file.transferTo()上传文件的运行时间:7ms
SpringMVC提供的上传文件方法的运行时间:4ms(最快)
前提:
准备两个tomcat服务器,并创建一个用于存放图片的web工程
在负责处理文件上传的项目中拷贝文件上传的必备jar包
commons-fileupload;commons-io;jersey-core;jersey-client
@RequestMapping("upload4")
public String testUpload4(MultipartFile file4) throws IOException {
System.out.println("跨服务器文件上传");
// 定义上传文件服务器路径
String path = "http://localhost:9090/uploads/";
String filename = file4.getOriginalFilename();
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid+"_"+filename;
// 创建客户端的对象
Client client = Client.create();
// 和图片服务器进行连接
WebResource webResource = client.resource(path+filename);
// 上传文件,将文件转为字节数组上传
webResource.put(file4.getBytes());
return "success";
}
系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
自定义异常实体类:
public class SysException extends Exception{
// 存储提示信息的
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public SysException(String message) {
this.message = message;
}
}
使用该注解有一个不好的地方就是:进行异常处理的方法必须与出错的方法在同一个Controller里面。
不能全局控制异常。每个类都要写一遍。
使用如下:
@Controller
@RequestMapping("test1")
public class ExceptionHandler {
@RequestMapping("testExceptionHandler")
public void testExceptionHandler() throws SysException {
throw new SysException("出错了!");
}
//SysException为自定义的异常实体类
@org.springframework.web.bind.annotation.ExceptionHandler({SysException.class})
public String exception(SysException e){
System.out.println(e.getMessage());
e.printStackTrace();
return "error";
}
}
这种方式可以进行全局的异常控制.
<!--配置异常处理器,class为类路径-->
<bean id="sysExceptionResolver" class="cn.itcast.exception.SysExceptionResolver"/>
public class SysExceptionResolver implements HandlerExceptionResolver{
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// 获取到异常对象
SysException e = null;
//如果抛出的是系统自定义异常则直接转换
if(ex instanceof SysException){
e = (SysException)ex;
}else{
//如果抛出的不是系统自定义异常则重新构造一个系统错误异常。
e = new SysException("系统正在维护....");
}
// 创建ModelAndView对象
ModelAndView mv = new ModelAndView();
mv.addObject("errorMsg",e.getMessage());
mv.setViewName("error");
return mv;
}
}
@ExceptionHandler 需要进行异常处理的方法必须与出错的方法在同一个Controller里面。那么当代码加入了 @ControllerAdvice,则不需要必须在同一个 controller 中了。这也是 Spring 3.2 带来的新特性。从名字上可以看出大体意思是控制器增强。 也就是说,@controlleradvice + @ ExceptionHandler 也可以实现全局的异常捕捉
@ControllerAdvice
public class ExceptionHandler {
//SysException为自定义的异常实体类
@org.springframework.web.bind.annotation.ExceptionHandler({SysException.class})
public String exception(SysException e){
System.out.println(e.getMessage());
e.printStackTrace();
return "error";
}
}
@Controller
@RequestMapping("/test")
public class UserController {
@RequestMapping("testControllerAdvice")
public void testControllerAdvice() throws SysException {
throw new SysException("测试ControllerAdvice");
}
}
HandlerInterceptor接口中有三个方法:
public class test implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle执行了");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle执行了");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion执行了");
}
}
path 的属性值“/**” 表示拦截所有路径;“/test” 表示拦截所有以 “/test” 结尾的路径;
"/test/*"表示拦截以test开头的路径;
<!--配置拦截器-->
<mvc:interceptors>
<!--配置拦截器-->
<mvc:interceptor>
<!--要拦截的具体的方法-->
<mvc:mapping path="/test/*"/>
<!--不要拦截的方法
<mvc:exclude-mapping path=""/>
-->
<!--配置拦截器对象-->
<bean class="cn.itcast.controller.interceptor.test" />
</mvc:interceptor>
</mvc:interceptors>
@Controller
@RequestMapping("test")
public class testController {
@RequestMapping("test1")
public String test1(){
System.out.println("控制器方法执行了");
return "success";
}
}
运行结果:
preHandle执行了 控制器方法执行了 postHandle执行了 success.jsp执行了... afterCompletion执行了
放行的含义是指,如果有下一个拦截器就执行下一个,如果该拦截器处于拦截器链的最后一个,则执行控制器中的方法。
拦截器执行顺序是按照Spring配置文件中定义的顺序而定的。
正确运行结果(postHandle和afterCompletion逆序执行):
错误运行结果(把第二个拦截器改为return false时):
所以当存在多个拦截器,其中一个拦截器的preHandle方法return false时,postHandle和控制器方法不执行;return false的那个拦截器的afterCompletion方法不执行,其它afterCompletion正常逆序执行。
在web.xml中使用tomcat的defaultservlet来处理静态资源
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/js/*</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>/images/*</url-pattern>
</servlet-mapping>
在springmvc.xml中使用<mvc:default-servlet-handler />
配置它后会在Spring MVC上下文中定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler, 它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理;如果不是静态资源的请求,才由DispatcherServlet继续处理。
在springmvc.xml中采用<mvc:resources />
<mvc:resources mapping="/images/**" location="/images/"/>
<mvc:resources mapping="/js/**" location="/js/" />
<mvc:resources mapping="/style/**" location="/style/" />
<mvc:resources mapping="*.html" location="/" />
注意:必须是webapp根目录下的路径
location:请求的资源地址。 mapping:映射后地址。
@Controller
@RequestMapping("user")
public class loginController {
//登陆页面
@RequestMapping("/login")
public String login(Model model)throws Exception{
return "login";
}
//登陆提交
@RequestMapping("/loginsubmit")
public String loginsubmit(HttpSession session, String userid, String pwd)throws Exception{
session.setAttribute("activeUser", userid);//将用户id存入session
return "redirect:/main.jsp";
}
//清除session并退出
@RequestMapping("/logout")
public String logout(HttpSession session)throws Exception{
//session过期
session.invalidate();
return "redirect:/index.jsp";
}
//模拟未登录状态下非法访问,直接访问/user/main
@RequestMapping("/main")
public String testMain(){
System.out.println("未登录,请先登录!");
return "forward:/main.jsp";
}
}
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//如果是登录页面则放行
if(request.getRequestURI().indexOf("user/loginsubmit")>=0){
return true;
}
HttpSession session = request.getSession();
//如果用户已登录也放行
if(session.getAttribute("activeUser")!=null){
return true;
}
//用户没有登录跳转到登录页面
request.getRequestDispatcher("/WEB-INF/pages/login.jsp").forward(request, response);
return false;
}
}
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="cn.itcast.controller.interceptor.LoginInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
评论