SpringMVC 上传方式与异常处理

SpringMVC 文件上传

所需依赖

SpringMVC 上传方式与异常处理
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>

传统方式上传文件

通用上传页

<h3> 文件上传 </h3>
<form action="user/fileupload" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"/><br/>
<input type="submit" value=" 上传文件 "/>
</form>

从 request 中获取文件

    @RequestMapping("/fileupload")
    public String fileuoload1(HttpServletRequest req) throws Exception {        StandardMultipartHttpServletRequest request = (StandardMultipartHttpServletRequest) req;
        System.out.println(" 文件上传...");
        //  使用 fileupload 组件完成文件上传
        //  上传的位置   获取最大的目录   即 webapp/WebRoot 目录
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        //  判断,该路径是否存在
        File file = new File(path);
        if (!file.exists()) {
            //  创建该文件夹
            file.mkdirs();
        }
        //  解析 request 对象,获取上传文件项
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        //  解析 request
        List<FileItem> items = upload.parseRequest(request);
        //  遍历
        for (FileItem item : items) {
            //  进行判断,当前 item 对象是否是上传文件项
            if (item.isFormField()) {
                //  说明普通表单向
            } else {
                //  说明上传文件项
                //  获取上传文件的名称
                String filename = item.getName();
                //  把文件的名称设置唯一值,uuid
                String uuid = UUID.randomUUID().toString().replace("-", "");
                filename = uuid + "_" + filename;
                //  完成文件上传
                item.write(new File(path, filename));
                //  删除临时文件(大于 10k 的都会生成临时文件   因此要删除)
                item.delete();
            }
        }
        return "success";
    }

SpringMVC 方式上传

文件解析器配置

springmvc.xml 配置文件解析器:

<!--  配置文件解析器对象,要求 id 名称必须是 multipartResolver  不能改 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/>
</bean>

更多配置

<!--  多部分文件上传  -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
     <property name="maxUploadSize" value="104857600" />
     <property name="maxInMemorySize" value="4096" />
     <property name="defaultEncoding" value="UTF-8"></property>
</bean>

上述配置文件单位是 Byte,10M 就是 10x1024x1024 相乘的值

上传大小异常处理

    <!-- 配置文件解析器对象 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="51200" />
    </bean>
    <!-- 1. 在文件上传解析时发现异常,此时还没有进入到 Controller 方法中  -->
    <bean id="exceptionResolver" class= "org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <!--  遇到 MaxUploadSizeExceededException 异常时,跳转到 error.jsp 页面  -->
                <prop key= "org.springframework.web.multipart.MaxUploadSizeExceededException">/error </prop>
            </props>
        </property>
    </bean>

上述即文件超过 50k 就跳转错误页面,报错就不会执行 Controller 里的代码了。

也可以自定义异常类,如下:

/**
 *  自定义异常处理器类
 */
public class ExceptionHandler implements HandlerExceptionResolver{
    /**
     *  处理上传文件大小超过限制抛出的异常
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest req,
            HttpServletResponse res, Object ob,Exception ex) {        ModelAndView mv=new ModelAndView();
        // 判断异常类型,来跳转不同页面
        if (ex instanceof MaxUploadSizeExceededException){ 
            // 指定错误信息
            mv.addObject("errormessage", " 上传文件过大 ");
            // 设置跳转视图
            mv.setViewName("userEdit");
            return mv;
        }  
        // 其他异常
        return null;
    }
}

然后将其加入 springmvc.xml 中即可

<bean class="com.cheng.exception.ExceptionHandler" />

由于 Tomcat 问题,7.0 系列版本在上传大文件如果超过大小限制会直接页面连接断开。这时可以不用 SpringMVC 自带的解析器限制文件大小,而是通过代码实现。

配置拦截器

public class FileUploadInterceptor extends HandlerInterceptorAdapter {
    private long maxSize;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 判断是否文件上传
        if(request!=null && ServletFileUpload.isMultipartContent(request)) {            ServletRequestContext ctx = new ServletRequestContext(request);
            // 获取上传文件尺寸大小
            long requestSize = ctx.contentLength();
            if (requestSize > maxSize) {
                // 当上传文件大小超过指定大小限制后,模拟抛出 MaxUploadSizeExceededException 异常
                throw new MaxUploadSizeExceededException(maxSize);
            }
        }
        return true;
    }
    public void setMaxSize(long maxSize) {
        this.maxSize = maxSize;
    }
}

配置解析器

    <!--  配置文件上传类型解析器  multipartResolver-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="cn.cheng.controller.FileUploadInterceptor">
                <!--  设定限制的文件上传大小  -->
                <property name="maxSize" value="51200"/>
            </bean>
        </mvc:interceptor>
    </mvc:interceptors>

上传格式异常处理

第一种在 Controller 方法中限制,但每次都写太过于麻烦,第二种使用拦截器,下列是后者:

public class FileTypeInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler)throws Exception {
        boolean flag= true;
        //  判断是否为文件上传请求
        if (request instanceof MultipartHttpServletRequest) {
            MultipartHttpServletRequest multipartRequest =
                    (MultipartHttpServletRequest) request;
            Map<String, MultipartFile> files =
                    multipartRequest.getFileMap();
            Iterator<String> iterator = files.keySet().iterator();
            // 对多部件请求资源进行遍历
            while (iterator.hasNext()) {                String formKey = (String) iterator.next();
                MultipartFile multipartFile =
                        multipartRequest.getFile(formKey);
                String filename=multipartFile.getOriginalFilename();
                // 判断是否为限制文件类型
                if (! checkFile(filename)) {
                    // 限制文件类型,请求转发到原始请求页面,并携带错误提示信息
                    request.setAttribute("errormessage", " 不支持的文件类型!");
                    request.getRequestDispatcher("/WEB-INF/pages/typeerror.jsp")
                            .forward(request, response);
                    flag= false;
                }
            }
        }
        return flag;
    }
    /**
     *  判断是否为允许的上传文件类型,true 表示允许
     */
    private boolean checkFile(String fileName) {
        // 设置允许上传文件类型
        String suffixList = "jpg,gif,png,ico,bmp,jpeg";
        //  获取文件后缀
        String suffix = fileName.substring(fileName.lastIndexOf(".")
                + 1, fileName.length());
        if (suffixList.contains(suffix.trim().toLowerCase())) {
            return true;
        }
        return false;
    }
}

xml 配置文件配置拦截器,注意先后顺序

    <mvc:interceptors>
        <mvc:interceptor>
            <!-- /** 表示所有 URL 和子 URL 路径  -->
            <mvc:mapping path="/**"/>
            <!--  配置自定义的文件上传类型限制拦截器  -->
            <bean class="cn.cheng.controller.FileTypeInterceptor"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="cn.cheng.controller.FileUploadInterceptor">
                <!--  设定限制的文件上传大小  -->
                <property name="maxSize" value="512000000000"/>
            </bean>
        </mvc:interceptor>
    </mvc:interceptors>

这样无论传多大都不会错,但是格式不正确会被跳转到错误页面。

拦截器返回 Json 数据

很多时候经常用 ajax 与 json 进行数据交互,因此并不一定每次出错都是跳转页面,返回 json 也很简单,直接写在拦截器 response 对象中即可

response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = null ;
try{    JSONObject res = new JSONObject();
    res.put("success","false");
    res.put("msg"," 上传文件类型错误 ");
    out = response.getWriter();
    out.append(res.toString());
    return false;
}
catch (Exception e){    e.printStackTrace();
    response.sendError(500);
    return false;
}

当然我们也可将其封装成工具类

public class SendMsgUtil {
    /**
     *  将某个对象转换成 json 格式并发送到客户端
     * @param response
     * @param obj
     * @throws Exception
     */
    public static void sendJsonMessage(HttpServletResponse response, Object obj) throws Exception {        response.setContentType("application/json; charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print(JSONObject.toJSONString(obj, SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteDateUseDateFormat));
        writer.close();
        response.flushBuffer();
    }
}

用 MultipartFile 的 transferTo

代码:

    @RequestMapping("/fileupload2")
    public String fileuoload2(HttpServletRequest request, MultipartFile upload) throws Exception {        System.out.println("springmvc 文件上传...");
        //  使用 fileupload 组件完成文件上传
        //  上传的位置
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        //  判断,该路径是否存在
        File file = new File(path);
        if (!file.exists()) {
            //  创建该文件夹
            file.mkdirs();
        }

        //  说明上传文件项
        //  获取上传文件的名称
        String filename = upload.getOriginalFilename();
        //  把文件的名称设置唯一值,uuid
        String uuid = UUID.randomUUID().toString().replace("-", "");
        filename = uuid + "_" + filename;
        //  完成文件上传  new File 中做目录   又文件名
        upload.transferTo(new File(path, filename));
        return "success";
    }

其中用文件参数类型用 CommonsMultipartFile 亦可,但是这时必须给参数 @RequestParam("file")注解指定所传文件项!

CommonsMultipartFile 的 getFileItem()

    @RequestMapping("/fileupload2")
    public String fileuoload2(HttpServletRequest request, @RequestParam("upload") CommonsMultipartFile upload) throws Exception {        System.out.println("springmvc 文件上传...");
        //  使用 fileupload 组件完成文件上传
        //  上传的位置
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        //  判断,该路径是否存在
        File file = new File(path);
        if (!file.exists()) {
            //  创建该文件夹
            file.mkdirs();
        }
        //  说明上传文件项
        //  获取上传文件的名称
        String filename = upload.getOriginalFilename();
        String uploadFileFileNameWithoutSpace = filename.replaceAll(" ", "");
        File targetFile = new File(path + File.separator, uploadFileFileNameWithoutSpace);
        // 判断目标文件存在就删除   其实只要保证文件名唯一就无需该操作
        if (targetFile.exists()) {            targetFile.delete();
        }
        upload.getFileItem().write(targetFile);
        return "success";
    }

其中 File.separator 等效于分隔符 "/",当然也可以拼接这个分隔符,按常理说 windows 系统路径分隔符是 \,使用时候需转义为\\,Linux 是/,因此 separator 可以对系统进行判断返回相应分隔符。

多文件上传

    @RequestMapping("fileupload22")
    public String springUpload(HttpServletRequest request) throws IllegalStateException, IOException {        long startTime = System.currentTimeMillis();
        // 将当前上下文初始化给   CommonsMutipartResolver (多部分解析器)
        //  上传的位置
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(                request.getSession().getServletContext());
        // 检查 form 中是否有 enctype="multipart/form-data"
        if (multipartResolver.isMultipart(request)) {
            // 将 request 变成多部分 request
            MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
            // 获取 multiRequest  中所有的文件名
            Iterator iter = multiRequest.getFileNames();
            while (iter.hasNext()) {
                // 一次遍历所有文件
                MultipartFile file = multiRequest.getFile(iter.next().toString());
                if (file != null) {                    String filepath = path + "/" + file.getOriginalFilename();
                    System.out.println(filepath);
                    // 上传
                    file.transferTo(new File(filepath));
                }
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println(" 运行时间:" + (endTime - startTime) + "ms");
        return "success";
    }

跨服务器上传

有专门的图片服务器 B,用户访问网站在 A 服务器,在 A 网站上传图片被转存到 B 服务器,A 服务器不作储存。首先我们需要两个 tomcat。

依赖:

<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-core</artifactId>
    <version>1.18.1</version>
</dependency>
<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-client</artifactId>
    <version>1.18.1</version>
</dependency>

实例代码

创建端口不同的 tomcat 的 web 项目,在 WEB-INF 里建 uploads 文件夹,修改 web.xml 配置文件

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <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>readonly</param-name>
      <param-value>false</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>
</web-app>

代码:

    @RequestMapping("/fileupload3")
    public String fileuoload3(MultipartFile upload) throws Exception {        System.out.println(" 跨服务器文件上传...");
        //  定义上传文件服务器路径
        String path = "http://localhost:9090/uploads/";
        //  说明上传文件项
        //  获取上传文件的名称
        String filename = upload.getOriginalFilename();
        //  把文件的名称设置唯一值,uuid
        String uuid = UUID.randomUUID().toString().replace("-", "");
        filename = uuid + "_" + filename;
        //  创建客户端的对象
        Client client = Client.create();
//         需要对文件名进行 URL 编码   若直接中文或空格上传会报错
        filename = URLEncoder.encode(filename, "utf-8");
        //  和图片服务器进行连接
        WebResource webResource = client.resource(path + filename);
        //  上传文件
        webResource.put(upload.getBytes());
        return "success";
    }

关于报错

409 可能原因是编译好的 web 目录下没有相应的例如以上的 uploads 文件夹,你项目的 web 目录即使有 ploads 文件夹文件夹但没一个文件,编译好后会自动忽略创建该目录,可在源码 web 目录文件夹内放任意文件即可。

403 可能是 web.xml 未按上述配置写入权限,另外如果以上代码文件名未 URL 编码,中文文件名文件上传亦会产生 403 错误。

文件上传注意事项

  1、为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于 WEB-INF 目录下。

  2、为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。

  3、为防止一个目录下面出现太多文件,要使用 hash 算法打散存储。

  4、要限制上传文件的最大值。

  5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。

目录
  • SpringMVC 文件上传
    • 传统方式上传文件
    • SpringMVC 方式上传
      • 文件解析器配置
      • 上传大小异常处理
      • 上传格式异常处理
      • 拦截器返回 Json 数据
      • 用 MultipartFile 的 transferTo
      • CommonsMultipartFile 的 getFileItem()
    • 多文件上传
    • 跨服务器上传
      • 实例代码
      • 关于报错
    • 文件上传注意事项
  • 目录
  • SpringMVC 文件上传
    • 传统方式上传文件
    • SpringMVC 方式上传
      • 文件解析器配置
      • 上传大小异常处理
      • 上传格式异常处理
      • 拦截器返回 Json 数据
      • 用 MultipartFile 的 transferTo
      • CommonsMultipartFile 的 getFileItem()
    • 多文件上传
    • 跨服务器上传
      • 实例代码
      • 关于报错
    • 文件上传注意事项
  • 手机扫描二维码访问

    本文标题:《SpringMVC 上传方式与异常处理》作者:极四维博客
    原文链接:https://cway.top/post/557.html
    特别注明外均为原创,转载请注明。

    分享到微信

    扫描二维码

    可在微信查看或分享至朋友圈。

    相关文章

    发表评论:

    ◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

    «    2024年10月    »
    123456
    78910111213
    14151617181920
    21222324252627
    28293031

    搜索

    控制面板

    您好,欢迎到访网站!
      查看权限

    最新留言

    文章归档

    • 订阅本站的 RSS 2.0 新闻聚合