使用SpringMVC完成多文件上传,以及设置文件类型

本文实现的功能: springMvc实现多文件上传,以及使用拦截器对文件类型,文件大小进行拦截. 如果上传的有一个是文件为空,就拦截.
本文运用的知识点: 在文章第4章总结.代码中也有详细的注释

一: 环境准备

1. 导入maven依赖:

 <dependencies>
        <!--springmvc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--servlet-api-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
            <scope>provided</scope>
        </dependency>
        <!--jsp-api-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!--文件上传-->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>
    </dependencies>

2.项目结构

在这里插入图片描述

3. web.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    
    <!--配置SpringMVC的前端控制器(核心控制器)-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <!--配置初始化参数,用于读取SpringMVC配置文件(指定配置文件的位置),使得dispatcherServlet被创建时,就加载配置文件,初始化Spring容器-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>

        <!--设置DispatcherServlet控制器,在服务器启动(应用加载)的时候创建对象,取值只能是非0正整数,表示启动顺序,数字越小优先级越高-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--配置dispatcherServlet的映射路径-->
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--把dispatcherServlet设置成  默认的缺省处理器 (覆盖Tomcat的默认处理器)-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--配置一个过滤器,解决乱码-->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <!--设置过滤器的属性值-->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <!--启动过滤器-->
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <!--过滤所有请求-->
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

4. springmvc.xml配置文件

<?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:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mcv="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/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">


    <!--开启mvc的注册驱动,实际上SpringMVC会自动加载组件,这里手动加载是程序健壮性-->
    <mvc:annotation-driven />
    <!--开启注解包扫描-->
    <context:component-scan base-package="com.zuoyueer"/>
	<!--静态资源的配置:因为DispatcherServlet不能处理静态资源,所以我们设置成让Tomcat处理静态资源-->
    <mcv:default-servlet-handler/>

    <!--配置视图解析器,用于将 逻辑视图 解析为 物理视图 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--指定视图文件的所在路径,前缀和后缀-->
        <!--前缀:指定文件夹的路径-->
        <property name="prefix" value="/"/>
        <!--后缀:指定文件名的后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

 <!--配置文件上传解析器:id必须是: multipartResolver
    文件上传的解析器 id是固定的,不能起别的名称,否则无法实现请求参数的绑定。
    (不光是文件,其他 字段也将无法绑定-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--设置上传文件的最大容量,单位是字节, -1 表示不限制,我把它注释了是为了使用自定义的拦截器-->
        <!-- <property name="maxUploadSize" value="-1"/>-->
        <!--设置编码,解决文件名乱码问题-->
        <property name="defaultEncoding" value="utf-8"/>
    </bean>

    <!--自定义拦截器(实际上是拦截链,类似于过滤器链)-->
    <mvc:interceptors>
        <!--每个拦截器的执行顺序,就是定义的顺序,在前面的先执行,被拦截了就不停止-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.zuoyueer.interceptor.FileSizeInterceptor">
                <property name="maxSize" value="30000"/>
            </bean>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.zuoyueer.interceptor.FileTypeInterceptor">
                <property name="suffixList" value="jpg,gif,png"/>
            </bean>
        </mvc:interceptor>
    </mvc:interceptors>
    
    <!--自定义异常处理器-->
    <bean class="com.zuoyueer.exception.MyExceptionHandler"/>
</beans>

5. 前端页面(一个请求页面,2个结果页面,结果页面太简单,就是打印一句话,我就不贴出来了)

<%--
  Created by IntelliJ IDEA.
  User: Zuoyueer
  Date: 2019/11/28
  Time: 15:32
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上传(普通方式)</title>
</head>
<h1>最基本的文件上传</h1>
<form action="${pageContext.request.contextPath}/file/upload" enctype="multipart/form-data" method="post">
    <input type="text" name="username"><br>
    <input type="file" name="files"> <br>
    <input type="submit" value="上传文件">
</form>
<h1>对上传文件的路径和名称进行处理</h1>
<form action="${pageContext.request.contextPath}/file/upload2" enctype="multipart/form-data" method="post">
    <input type="file" name="files"> <br>
    <input type="submit" value="上传文件">
</form>
<h1>多文件上传,name属性值必须一样</h1>
<form action="${pageContext.request.contextPath}/file/upload3" enctype="multipart/form-data" method="post">
    <input type="file" name="files"> <br>
    <input type="file" name="files"> <br>
    <input type="file" name="files"> <br>
    <input type="submit" value="上传文件">
</form>
</body>
</html>

二:控制器 FileController.java

package com.zuoyueer.conntroller;

import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

/**
 * @author Zuoyueer
 * Date: 2019/11/28
 * Time: 15:37
 * @projectName Framework
 * @description: 文件控制器
 */
@Controller
@RequestMapping("file")
public class FileController {
    /**
     * 同时上传多个文件
     */
    @RequestMapping("upload3")
    public String upload3(@RequestParam MultipartFile[] files, HttpServletRequest request) throws IOException {
        //直接遍历,不需要做非空判断,因为files不可能为空,即使不选择文件也会有其他内容
        for (MultipartFile file : files) {
            String fileName="";
            String originalFilename = file.getOriginalFilename();
            //getName获取的是表单的name属性值,别用错了
            String name = file.getName();
            System.out.println(originalFilename+"   :   "+name);
            String uuid = UUID.randomUUID().toString().replace("_", "").toUpperCase();
            fileName= uuid+"_"+originalFilename;
            String basePath = request.getServletContext().getRealPath("file3");
            String datePath = new SimpleDateFormat("yyy-MM-dd").format(new Date());
            File destDir = new File(basePath + "/" + datePath);
            if(!destDir.exists()){
                destDir.mkdirs();
            }
            File destFile = new File(destDir, fileName);
            file.transferTo(destFile);
        }
        return "success";
    }

    /**
     * 对上传文件的路径和名称进行处理
     */
    @RequestMapping("upload2")
    public String upload2(MultipartFile files, HttpServletRequest request) throws IOException {
        //定义文件名
        String fileName = "";
        //获取原始的文件名
        String originalFilename = files.getOriginalFilename();
        //防止文件名重复,设置随机文件名,toUpperCase将字母大写
        String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
        //得到最终的我文件名
        fileName = uuid + "_" + originalFilename;
        //设置存储路径
        String basePath = request.getServletContext().getRealPath("files2");
        //解决同一个文件夹中文件过多问题,每天一个新的文件夹
        String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        //创建文件夹
        File destDir = new File(basePath + "/" + datePath);
        if (!destDir.exists()) {
            destDir.mkdirs();
        }
        //上传文件
        File destFile = new File(destDir, fileName);
        files.transferTo(destFile);
        //跳转
        return "success";
    }

    /**
     * 最基本的文件上传
     */
    @RequestMapping("upload")
    public String upload(MultipartFile files, HttpServletRequest request) throws IOException {
        //设置保存路径
        String path = request.getServletContext().getRealPath("files");
        //根据路径创建保存文件夹
        File destDir = new File(path);
        if (!destDir.exists()) {
            destDir.mkdirs();
        }
        //保存文件
        File destFile = new File(destDir, files.getOriginalFilename());
        files.transferTo(destFile);
        //跳转
        return "success";
    }
}


实际上,到这里,就以及能实现,文件的上传了,只不过有一些问题需要解决

  • 文件类型进行限制
  • 文件的大小进行限制
  • 特别是多文件上传的时候,要实现即使有一个文件为空,就阻止提交.
  • 以及不满足以上情况的异常处理

三 .拦截器的实现

文件类型拦截器

package com.zuoyueer.interceptor;

import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author Zuoyueer
 * Date: 2019/11/28
 * Time: 17:54
 * @projectName Framework
 * @description: 上传文件类型拦截器
 */
public class FileTypeInterceptor extends HandlerInterceptorAdapter {
    //允许上传的文件类型
    private String suffixList;
	//set方法的目的是为了,能在配置文件中指定文件类型,实现解耦
    public void setSuffixList(String suffixList) {
        this.suffixList = suffixList;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        boolean flag = true;
        //判断是否是文件上传请求
        if (request instanceof MultipartHttpServletRequest) {
            // 转换request,解析出request中的文件
            MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
            // 获取文件集合
            List<MultipartFile> files = multipartHttpServletRequest.getFiles("files");
            //遍历集合中的文件
            for (MultipartFile file : files) {
                //如果有一个文件为空,就不允许上传
                if (file.isEmpty()){
                    request.setAttribute("errorMessage", "文件为空");
                    request.getRequestDispatcher("/error.jsp").forward(request, response);
                    //直接拦截,不继续执行下面的其他判断了
                    return  false;
                }
                String originalFilename = file.getOriginalFilename();
                //对文件类型进行检查,不符合就拦截
                if (!checkFile(originalFilename)) {
                    request.setAttribute("errorMessage", "不支持的文件类型");
                    request.getRequestDispatcher("/error.jsp").forward(request, response);
                    flag = false;
                }
            }
        }
        return flag;
    }
	//检查后缀名是否符合要求
    private boolean checkFile(String fileName) {
        //获取文件后缀(文件类型)
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        if (suffixList.contains(suffix.trim().toLowerCase())) {
            return true;
        }
        return false;
    }
}

文件大小拦截器

package com.zuoyueer.interceptor;

import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.servlet.ServletRequestContext;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author Zuoyueer
 * Date: 2019/11/28
 * Time: 19:47
 * @projectName Framework
 * @description: 上传文件大小拦截器
 */
public class FileSizeInterceptor extends HandlerInterceptorAdapter {
    private long maxSize;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (request!=null && ServletFileUpload.isMultipartContent(request)){
            //Servlet请求的上下文信息,都封装在ServletRequestContext对象中
            ServletRequestContext servletRequestContext = new ServletRequestContext(request);
            System.out.println(servletRequestContext.contentLength());
            //获取请求内容的大小,包括请求中的全部数据
            long contentLength = servletRequestContext.contentLength();
            if (contentLength>maxSize){
                throw new MaxUploadSizeExceededException(maxSize);
            }
        }
        return true;
    }

    public void setMaxSize(long maxSize) {
        this.maxSize = maxSize;
    }
}


异常处理器

package com.zuoyueer.exception;

import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author Zuoyueer
 * Date: 2019/11/28
 * Time: 18:31
 * @projectName Framework
 * @description: 自定义异常处理器
 */
public class MyExceptionHandler implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        ModelAndView modelAndView = new ModelAndView();
        //上传文件大小超过限制抛出的异常处理
        if (e instanceof MaxUploadSizeExceededException){
            modelAndView.addObject("errorMessage", "文件过大");
            modelAndView.setViewName("error");
        }
        //其他异常
        return modelAndView ;
    }
}

四: 总结

MultipartFile接口

使用之前必须导入文件上传依赖和配置文件上传解析器
MultipartResolver 用于处理文件上传,当收到请求时 DispatcherServlet 的 checkMultipart() 方法会调用 MultipartResolver 的 isMultipart() 方法判断请求中是否包含文件。如果请求数据中包含文件,则调用 MultipartResolver 的 resolveMultipart() 方法对请求的数据进行解析,然后将文件数据解析成 MultipartFile 并封装在 MultipartHttpServletRequest (继承了 HttpServletRequest) 对象中,最后传递给 Controller,在 MultipartResolver 接口中有如下方法:

  • boolean isMultipart(HttpServletRequest request); // 是否是 multipart
  • MultipartHttpServletRequest resolveMultipart(HttpServletRequest request); // 解析请求
  • void cleanupMultipart(MultipartHttpServletRequest request);

MultipartFile 封装了请求数据中的文件,此时这个文件存储在内存中或临时的磁盘文件中,需要将其转存到一个合适的位置,因为请求结束后临时存储将被清空。在 MultipartFile 接口中有如下方法:

  • String getName(); // 获取参数的名称
  • String getOriginalFilename(); // 获取文件的原名称
  • String getContentType(); // 文件内容的类型
  • boolean isEmpty(); // 文件是否为空
  • long getSize(); // 文件大小
  • byte[] getBytes(); // 将文件内容以字节数组的形式返回
  • InputStream getInputStream(); // 将文件内容以输入流的形式返回
  • void transferTo(File dest); // 将文件内容传输到指定文件中

在文件上传解析器中,还能配置很多属性,其中有一个resolveLazily属性,是懒加载,能够实现文件没有上传(文件过大,网速慢)完也能执行接下来的操作.可惜,没有文件类型的属性,使用,我们必须自定义拦截器来限制文件类型
在这里插入图片描述

MultipartHttpServletRequest

这个接口实际上是对request接口的增强,是文件解析器帮我们完成的封装, 我们直接使用它里面的方法,就能获取到请求中的各种内容.
它的方法有很多,与文件上传相关常用的,就是上面拦截器中使用的那几个~

ServletRequestContext

这个实现类提供对HTTP Servlet的请求所需的请求信息的访问,我之所用这个纯粹是为了用以下而已
实际上是multipartHttpServletRequest.getContentLength()也能获取请求体的大小.


以下博客对我帮助很大,感谢大佬的分享!

https://www.iteye.com/blog/exceptioneye-1314958
https://www.cnblogs.com/tengyunhao/p/7670293.html

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值