Spring MVC 总结 03 文件上传

 

第2章 SpringMVC实现文件上传

2.1 文件上传的回顾

2.1.1 文件上传的必要前提

1、form表单的enctype取值必须是:multipart/form-data(指定传输数据为二进制数据,例如图片、mp3、文件。http请求中的multipart/form-data,会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。 既可以上传键值对,也可以上传文件)
默认值:application/x-www-form-urlencoded(会将表单内的数据转换为键值对)
2、method属性取值必须是post:如果是get会把你请求的东西放在地址栏上,地址栏的大小是有限制的,装不了多少数据;大数据的提交必须选post。
3、提供一个文件选择域<input type=”file” /> 

2.1.2 文件上传的原理分析

当 form 表单的 enctype 取值不是默认值后,request.getParameter()将失效。
enctype=”application/x-www-form-urlencoded”时,form 表单的正文内容是:key=value&key=value&key=value 
当form表单的enctype 取值为Mutilpart/form-data时,请求正文内容就变成:
每一部分都是 MIME 类型描述的正文,如下:

-----------------------------7de1a433602ac   分界符 
Content-Disposition: form-data; name="userName"  协议头 
  
aaa              协议的正文 
-----------------------------7de1a433602ac 
Content-Disposition: form-data; name="file"; 
filename="C:\Users\zhy\Desktop\fileupload_demofile\b.txt" 
Content-Type: text/plain         协议的类型(MIME 类型) 
 
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 
-----------------------------7de1a433602ac-- 

2.1.3 借助第三方组件实现文件上传

这些组件给我们提供了api,可以很方面的解析上传文件的请求体,不需要我们过于细节的去解析那个请求体(不需要我们知道请求体的格式也能正确解析)。这个组件是Apache提供的。

        <!-- 导入文件上传的jar包 -->
        <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>

2.1.4 传统文件上传方式:

借助了2.1.3的第三方组件;需要解析整个请求体。 
前端提交表单,其实所有的内容最终全部会被封装到request这个对象中。所以得解析request从而拿到你上传文件的这个选项。 

前端:

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

后端:

    @RequestMapping("/fileUpload1")
    public String fileUpload1(HttpServletRequest request) throws Exception {
        System.out.println("fileUpload1...");
        // 使用fileupload组件完成文件上传
        // 上传位置
        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;
                // 完成文件上传(往path路径传名为filename的这个文件)
                item.write(new File(path, filename));
                // 删除临时文件
                // (这个上传会帮你产生临时文件,一旦你上传文件的大小大于10KB,就有临时文件,当把文件传完你可以将其删掉;
                // 如果小于10KB,它就在内存当中帮你生成缓存的一个文件,这个不用处理)
                item.delete();
            }
        }
        return "success";
    }

特别注意:

配置Tomcat服务器的时候一定要选择这个:

不要选上面那个,不然文件不会上传到工程目录下target目录里去,而是会跑到Tomcat安装路径的webapps,具体是什么路径可以自己断点调试看看就知道了。

2.2 SpringMVC传统方式的文件上传

2.2.1 原理分析:

说明:配置好文件解析器后,会通过参数绑定的方式将文件上传项赋值给controller方法里的MultipartFile对象。与传统方式相比的优势,不需要自己作request对象的解析工作了。
注意:
1、前端页面的input框得name为upload,后端controller方法也必须为upload,否则参数绑定不上。
2、配置文件解析器的时候bead的id值必须为multipartResolver,不能随意改动,因为底层框架默认是用这个id名的。
2的补充说明:文件上传的解析器 id是固定的,不能起别的名称,否则无法实现请求参数的绑定。(不光是文件,其他 字段也将无法绑定)

2.2.2 具体操作

在springmvc.xml配置文件中配置,

    <!--配置文件解析器对象 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--value单位大小为字节,配置10MB=10*1024*1024-->
        <property name="maxUploadSize" value="10485760"/>
    </bean>

编写controller方法,

    @RequestMapping("/fileUpload2")
    public String fileUpload2(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();//拿到传统的名称,就是你文件的名字
        System.out.println(upload.getName());
        // 把文件的名称设置唯一值,uuid(好处:以后上传相同的文件不会覆盖)
        String uuid = UUID.randomUUID().toString().replace("-", "");
        filename = uuid + "_" + filename;
        // 完成文件上传(往path路径传名为filename的这个文件)
        upload.transferTo(new File(path, filename));//不需要删除临时文件了,框架帮你处理过
        return "success";
    }

2.2.3 特别注意:

SpringMVC传统方式上传文件,也需要上面的第三方提供的组件(

<!-- 导入文件上传的jar包 -->
<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>

),因为文件解析器(multipartResolver)需要用到里面的jar包,只要配置了这个文件解析器就不需要导入那个jar包,下面跨服务器上传同理也需要。

 2.3 SpringMVC跨服务器方式的文件上传

示意图:

2.3.1 搭建图片服务器

配置两个Tomcat服务器
第一个Tomat服务器不变作为应用服务器,
第二个记得改端口等参数,作为图片服务器(文件服务器)

配置好之后,再新建一个Module:
这个Module不需要做太多的事,只需要增加一个文件夹一会上传的文件就到这里,然后就是修改一下index.jsp的提示语即可。

最后再把这个项目部署到上面配置的那个Tomcat图片服务器里。

导入jar包,是由sun公司提供的

        <!--跨服务器文件上传-->
        <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>

2.3.2 编写后端代码

    @RequestMapping("/fileUpload3")
    public String fileUpload3(MultipartFile upload) throws Exception {
        System.out.println("SpringMVC跨服务器方式的文件上传...");
        // 定义上传文件服务器路径
        String path = "http://localhost:9090/fileuploadserver_war_exploded/uploads/";
        // 说明是上传文件项
        // 获取上传文件的名称
        String filename = upload.getOriginalFilename();
        // 把文件的名称设置唯一值,uuid
        String uuid = UUID.randomUUID().toString().replace("-", "");
        filename = uuid + "_" + filename;

        // 创建客户端的对象,注意选对jar包
        Client client = Client.create();
        // 和图片服务器进行连接
        WebResource webResource = client.resource(path + filename);
        // 上传文件,需要个是一个byte[]数组(字节数组)
        webResource.put(upload.getBytes());
        return "success";
    }

2.3.3 启动两个Tomcat服务器进行测试

1、报错UniformInterfaceException 405

Message 
Request processing failed; nested exception is com.sun.jersey.api.client.UniformInterfaceException: PUT http://localhost:9090/uploads/92f40f26e58e47ffacb304f7243fad55_girl8.jpg returned a response status of 405 Method Not Allowed
Root Cause
com.sun.jersey.api.client.UniformInterfaceException: PUT http://localhost:9090/uploads/92f40f26e58e47ffacb304f7243fad55_girl8.jpg returned a response status of 405 Method Not Allowed

原因:Tomcat权限问题。
打开tomcat/conf/web.xml找到相关配置信息可以看到

  <!--   readonly            Is this context "read only", so HTTP           -->
  <!--                       commands like PUT and DELETE are               -->
  <!--                       rejected?  [true]                              -->

通过注释可以知道,默认情况下我们进行put或者delete操作时候,服务器是拒绝访问的,所以想要向服务器上传文件必须将readonly属性设置为false,自己手动添加这个属性并设置为false,允许读写操作:

代码:

        <init-param>
            <param-name>readonly</param-name>
            <param-value>false</param-value>
        </init-param>

2、报错 409

Request processing failed; nested exception is com.sun.jersey.api.client.UniformInterfaceException: PUT http://localhost:9090/fileuploadserver_war_exploded/uploads/55244f1d4d214cc78ea38edcae046de4_girl8.jpg returned a response status of 409 Conflict

原因:上面的新工程在webapp目录下新建的一个空的uploads文件夹,如果文件夹下没有内容是空的话,部署的时候是不会在target目录的响应位置创建uploads文件夹的。
解决方式:
1、在uploads文件夹下随便创个文件,一会部署的时候就会在target下生成uploads文件夹了;
2、手动去target目录下的相应位置创建一个;
3、后端自己写代码判断不存在这个文件夹就创建一个。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值