(四)Spring MVC 入门教程之文件上传

一、原理分析和环境搭建

1、文件上传原理分析

要想实现 文件上传 的功能,其必要前提和原理分析如下:

######## 文件上传的必要前提


1、form 表单的 enctype 取值必须是:multipart/form-data (默认值是:application/x-www-form-urlencoded)
2、method 属性取值必须是 Post 请求
3、提供一个文件选择域,即:<input type=”file”/>
######## 文件上传的原理分析
当 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"     协议头
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa                    协议的正文
-----------------------------7de1a433602ac          分界符
Content-Disposition: form-data; name="file"; filename="C:\Users\guoshizhan\Desktop\fileupload_testFile\b.txt"
Content-Type: text/plain                            协议的类型(MIME 类型)
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb                    协议的正文
-----------------------------7de1a433602ac          分界符
######## 借助第三方组件实现文件上传

使用 Commons-fileupload 组件实现文件上传
需要导入该组件相应的支撑 jar 包:Commons-fileupload 和 commons-io

commons-io 不属于文件上传组件的开发 jar 文件
但 Commons-fileupload 组件从 1.1 版本开始,它工作时需要 commons-io 包的支持

上述介绍了一下文件上传的原理,那么接下来就进行环境搭建。 接着往下看!!!

2、新建 Maven 工程

第一步:创建 Maven 项目(记得勾选 maven-archetype-webapp),完善相应包结构。 然后导入如下依赖:

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <spring.version>5.0.2.RELEASE</spring.version>
</properties>

<dependencies>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
        <scope>provided</scope>
    </dependency>

</dependencies>

3、编写相关配置文件

第二步:编写 web.xml 和 SpringMvc.xml 配置文件。 代码如下:

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <!--配置前端控制器-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <!-- 以下的配置是让 Spring 在项目启动时去加载 springMVC.xml 配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springMvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <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>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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">

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="club.guoshizhan"/>

    <!-- 视图解析器对象,让 controller 里面的方法知道去哪个地方找页面 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 配置哪些静态资源不拦截 -->
    <mvc:resources location="/css/" mapping="/css/**"/>
    <mvc:resources location="/images/" mapping="/images/**"/>
    <mvc:resources location="/js/" mapping="/js/**"/>

    <!-- 开启 SpringMVC 框架注解的支持 -->
    <mvc:annotation-driven />

</beans>

4、编写相关页面

第三步:编写 index.jsp 和 success.jsp 页面。 代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上传</title>
</head>
<body>
<h3>文件上传</h3>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>文件上传</title>
</head>
<body>
    <h3>上传成功!</h3>
</body>
</html>

5、编写 controller 控制器

第四步:编写 UploadController 控制器类。 代码如下:

package club.guoshizhan.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/file")
public class UploadController {

    // 文件上传
    @RequestMapping("/upload1")
    public String fileupload() {
        System.out.println("File upload is running……");
        return "success";
    }

}

TIPS: 原理分析和环境搭建 到此结束!!!

二、传统文件上传

1、添加依赖包

第一步:把下列依赖添加到 pom.xml 文件中。 依赖如下:

<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、编写 index.jsp 页面

第二步:重写 index.jsp 页面文件。 代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上传</title>
</head>
<body>

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

</body>
</html>

3、编写 controller 控制器

第三步:重写 UploadController 控制器类。 代码如下:

package club.guoshizhan.controller;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.List;
import java.util.UUID;

@Controller
@RequestMapping("/file")
public class UploadController {

    /**
     * 传统文件上传
     */
    @RequestMapping("/upload1")
    public String fileupload1(HttpServletRequest request) throws Exception {
        System.out.println("File upload is running……");

        // 使用 fileupload 组件来完成文件上传,path 是上传后文件存放的位置
        String path = "C:\\Users\\guoshizhan\\Desktop\\SpringMvc\\uploads";

        // 判断该路径是否存在
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();    // 创建该文件夹
        }

        // 解析 request 对象,从而获取上传文件项
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        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));
                // 删除临时文件
                item.delete();
            }
        }

        return "success";
    }

}

最后:重新启动 tomcat 服务器,然后进行测试即可~~
最终图片会存放在指定的位置(我的代码里使用了绝对位置,一般不建议这样使用)

三、SpringMVC 文件上传

1、编写 index.jsp 页面

第一步:重写 index.jsp 页面文件。 代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上传</title>
</head>
<body>

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

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

</body>
</html>

2、配置文件解析器对象

第二步:在 SpringMvc.xml 文件中配置文件解析器对象。 代码如下:

<!-- 配置文件解析器对象,id 的值必须是 multiPartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 配置上传文件的最大值,单位是字节 -->
    <property name="maxUploadSize" value="10485760" />
</bean>

3、添加上传方法

第三步:在 UploadController 控制器类中新加 fileupload2 方法。 代码如下:

/**
 * SpringMVC 文件上传,参数中的 upload 须和表单中的 name 属性的值一样
 */
@RequestMapping("/upload2")
public String fileupload2(HttpServletRequest request, MultipartFile upload) throws Exception {
    System.out.println("File upload is running……");
    // 此处的路径:D:\Programs\Java\Tomcat\apache-tomcat-9.0.36\webapps\ROOT\uploads
    // 暂时没有搞懂,它为什么不会在当前项目的 target 目录里面生成这个 upload 目录,却到 tomcat 的目录下了
    String path = request.getSession().getServletContext().getRealPath("/uploads/");
    File file = new File(path);
    if (!file.exists()) {
        file.mkdirs();
    }

    String filename = upload.getOriginalFilename();
    String uuid = UUID.randomUUID().toString().replace("-", "");
    filename = uuid + "_" + filename;
    upload.transferTo(new File(path, filename));

    return "success";
}

最后:重新启动 tomcat 服务器,然后进行测试即可~~

四、跨服务器文件上传

为什么会有 跨服务器上传 这样的功能呢? 解答如下:

######## 在实际开发中,我们会有很多处理不同功能的服务器。例如:

应用服务器 ==> 负责部署我们的应用
数据库服务器 ==> 运行我们的数据库
缓存和消息服务器 ==> 负责处理大并发访问的
缓存和消息文件服务器 ==> 负责存储用户上传文件的服务器


# 注意:此处说的不是服务器集群

1、跨服务器文件上传原理

跨服务器文件上传 的原理图如下:

2、搭建图片服务器

那么问题来了,我们如何去搞一台图片服务器呢(PS:使用 tomcat 模拟出一个图片服务器)? 这么办: 创建 Maven 项目(记得勾选 maven-archetype-webapp ),然后给这个 web 项目配置好 tomcat ,即配置端口号等。然后运行 tomcat 即可。详细步骤如下:


第一步:创建 Maven 项目(记得勾选 maven-archetype-webapp)。然后编写 index.jsp 页面。 代码如下:

<html>
<body>
<h2>Hello PicServer!</h2>
</body>
</html>

第二步:给新建的项目配置 tomcat 服务器(这个就是我们的图片服务器)。 具体配置步骤如下:

第三步:启动 tomcat 服务器。 结果如下:

若能够看到上述页面,那么图片服务器搭建成功!

3、导入相关依赖

那么接下来,我们回到 SpringMVC 项目里,开始真正的 跨服务器上传 。具体步骤如下:


第一步:在 pom.xml 文件中导入相关依赖。 代码如下:

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

4、编写 index.jsp 页面

第二步:重写 index.jsp 页面文件。 代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上传</title>
</head>
<body>

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

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

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

</body>
</html>

5、添加上传方法

第三步:在 UploadController 控制器类中新加 fileupload3 方法。 代码如下:

/**
 * 跨服务器文件上传
 */
@RequestMapping("/upload3")
public String fileupload3(MultipartFile upload) throws Exception {
    System.out.println("File upload across different server is running……");

    // 定义上传文件服务器路径,传到我们的图片服务器
    String path = "http://localhost:9090/uploads/";
    String filename = upload.getOriginalFilename();
    String uuid = UUID.randomUUID().toString().replace("-", "");
    filename = uuid + "_" + filename;

    // 创建客户端的对象
    Client client = Client.create();
    // 和图片服务器进行连接
    WebResource webResource = client.resource(path + filename);
    // 上传文件
    webResource.put(upload.getBytes());

    return "success";
}

6、启动 tomcat 进行测试

启动 tomcat 服务器,进入浏览器查看结果(可能 500 错误)。 解决办法如下:

也可能出现 405 Method Not Allowed 错误。 解决办法如下:

<servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    
    <!-- 使得服务器允许文件写入。-->
    <init-param>
        <param-name>readonly</param-name>
        <param-value>false</param-value>
    </init-param>

    <init-param>
        <param-name>debug</param-name>
        <param-value>0</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>

如果没有报错,那么恭喜你。跨服务器上传成功了。 结果如下:

  • 文章作者:root
  • 创建时间:2020-09-16 09:03:28
  • 更新时间:2022-01-03 14:54:30
  • 版权声明:本文为博主原创文章,未经博主允许不得转载!
请 在 评 论 区 留 言 哦 ~~~
1024