springMVC的文件上传

  上传文件是一个web应用很常用的一个功能,比如在一些社交网站上传些图片、视频等。下面我们来看一下,如何在内置tomcat整合springMVC的环境下,利用配置类和注解方式完成文件上传。

  第一步:导入依赖,在配置类中完成上传文件组件的注册。

  先创建一个maven工程,导入tomcat、spring、springMVC的依赖(详见之前的博文),另外,导入上传文件相关的依赖:
  

    <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
    </dependency>
    <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.3</version>
    </dependency>

 

  在配置类中,添加上传文件类组件的注册:

    @Configuration
    @EnableWebMvc
    @ComponentScan(basePackageClasses = WebApplicationConfig.class)
    public class WebApplicationConfig implements WebMvcConfigurer {
        @Bean
        public MultipartResolver multipartResolver() {
            CommonsMultipartResolver multipartResolver =
                    new CommonsMultipartResolver();
            //设置上传文件大小限制
            multipartResolver.setMaxUploadSize(10240000);
            return multipartResolver;
        }
    }

 

  这里需要注意,方法名只能是multipartResolver。

  第二步,编写客户端html页面。

  在resources目录下的static/html中,新建文件夹image,用于存放上传文件。新建html页面:upload.html,创建上传文件表单。创建表单时,需要注意两点:

    1、该表单必须以POST方式提交,因为只有POST方式才支持客户端二进制方式的传输。

    2、在form表单标签中,需要添加enctype="multipart/form-data" 属性。

  添加enctype="multipart/form-data" 属性的原因是:默认情况下,form表单提交给服务器的数据是以“application/x-www-form-urlencoded”方式提交的。这样的格式在请求信息的消息体中,数据格式为“name=tom&tel=13029382329&password=123”。这样,如果在表单中有文件表单,那么,提交给服务器的只是文件表单选中文件的文件名:“face=1.jpg”。很明显,这样的数据是无法完成文件上传的,上传文件需将客户端的文件以二进制方式提交给服务器。

  加上enctype="multipart/form-data"属性后,消息体的格式就不再是键值对方式了,格式为:

    ------WebKitFormBoundaryqgkaBn8IHJCuNmiW
    Content-Disposition: form-data; name="password"
    letmein01
    ------WebKitFormBoundaryqgkaBn8IHJCuNmiW
    Content-Disposition: form-data; name="profilePicture"; filename="me.jpg"
    Content-Type: image/jpeg
    ……
    ……
    ------WebKitFormBoundaryqgkaBn8IHJCuNmiW--

 

  这样,才能将客户端文件的二进制数据提交给服务器。

  update.html完整的代码为:

    <form action="/upload/add" method="post" enctype="multipart/form-data">
        姓名:<input type="text" name="name"><br>
        头像:<input type="file" name="face"><br>
        <input type="submit" value="提交">
    </form>

 

  第三步,创建应用控制器,处理客户端上传文件的数据。

  在这里,需要注意,由于使用的是内置tomcat,所以上传的文件应该在static/html/image目录中,对于该路径的物理路径的获取,可以通过线程来实现。

  应用控制器代码如下:

    @RestController
    @RequestMapping("upload")
    public class UploadController {

       @RequestMapping("add")
        public String add(String name,@RequestParam("face") MultipartFile mf) throws Exception {
                //得到上传文件名
                String fileName = mf.getOriginalFilename();

                //通过当前线程,得到上传文件存放目录的真实路径
                URL url = Thread.currentThread().getContextClassLoader()
                        .getResources("static/html/image").nextElement();
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");

                //将上传文件的二进制数据,写入指定的文件
                mf.transferTo(new File(filePath + "/" + fileName));

           return "姓名:"+name+"  上传文件名:"+fileName;
        }
    }

  考虑到每次上传文件不能同名,如果同名会造成上传文件的彼些覆盖。所以,需要对上传的文件进行重命名。另外,在项目中,可能会有多个应用控制器都有上传文件的业务。所以,可以将上传文件封装成工具类,便于代码重用。

    public class UploadUtil {
        /**
         * 上传文件
         * @param mf  上传文件对象
         * @param dirPath 上传文件存放的目录
         * @return 重命名后的上传文件名
         */
        public static String upload(MultipartFile mf,String dirPath){
            //得到上传文件名
            String fileName = mf.getOriginalFilename();
            try {
                //重命名文件
                fileName = System.currentTimeMillis()
                        + fileName.substring(fileName.lastIndexOf("."));
                //得到上传文件存放目录的真实路径
                URL url = Thread.currentThread().getContextClassLoader()
                        .getResources(dirPath).nextElement();
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");

                //将上传文件的二进制数据,写入指定的文件
                mf.transferTo(new File(filePath + "/" + fileName));
            }catch (Exception e){
                e.printStackTrace();
            }

            return fileName;
        }
    }

 

  这样,在应用控制器中,通过简单的调用,就能实现文件上传:

    @RestController
    @RequestMapping("upload")
    public class UploadController {

       @RequestMapping("add")
        public String add(String name,@RequestParam("face") MultipartFile mf) throws Exception {
            String fileName = UploadUtil.upload(mf, "static/html/image");

           return "姓名:"+name+"  上传文件名:"+fileName;
        }
    }

 

  好了,现在启动服务器,打开浏览器访问upload.html页面,填写姓名,选择上传文件,提交表单。就可以完成文件上传。注意,上传的文件在maven工程的target目录下(这才是maven工程的运行环境)。在target/classes/static/html/image目录,可以看到上传的文件。

  另外,还有个地方需要注意。如果上传文件和表单中的其他数据需要封装成实体对象时,实体类中存放的上传文件通常是以字符串方式存放文件名的。所以,不能把上传文件的表单名和实体类相关的属性名定义成一样。因为实体类的属性名是字符串,而上传文件后服务器必须以MultipartFile类型来接收上传文件。由于类型不一致,会抛出异常。

posted @ 2021-01-29 17:45  朗沃张成峰  阅读(73)  评论(0)    收藏  举报