【Spring MVC学习笔记 八】SpringMVC实现文件上传和下载

简介: 【Spring MVC学习笔记 八】SpringMVC实现文件上传和下载

本篇Blog继续拓展了解SpringMVC框架是如何实现文件上传和下载的。文件上传和下载是项目开发中最常见的功能。我们来实践一下,当然提前准备好实践环境:

并且在其中导入相关的Maven依赖,这里包含commons-fileupload

全量依赖如下:

<dependencies>
        <!-- commons-fileupload -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>
        <!-- jackson包引入-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.1</version>
        </dependency>
        <!--Spring MVC框架依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.9</version>
        </dependency>
        <!--JSP相关依赖-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!--servlet相关依赖-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <!--单元测试相关依赖-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

接着是我们的传统艺能,注册SpringMVC过滤器来拦截请求

<?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"
         xmlns:context="http://www.springframework.org/schema/context"
         xmlns:mvc="http://www.springframework.org/schema/mvc"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--配置前端控制器-->
    <servlet>
        <!--1.注册DispatcherServlet-->
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!--关联一个springMVC的配置文件:【servlet-name】-servlet.xml-->
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--启动级别-1,在Tomcat启动时就初始化Spring容器-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--/ 匹配所有的请求;(不包括.jsp)-->
    <!--/* 匹配所有的请求;(包括.jsp)-->
    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

文件上传

文件上传是项目开发中最常见的功能之一 ,SpringMVC 可以很好的支持文件上传。为了能上传文件,表单提交和SpringMVC框架必须满足如下两个条件:

  • 表单提交的method需要设置为POST,并将enctype设置为multipart/form-data。一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器。
  • SpringMVC的文件上传需要依赖Apache Commons FileUpload的组件,Spring MVC 为文件上传提供了直接的支持,这种支持是用即插即用的 MultipartResolver实现的。Spring MVC 使用 Apache Commons FileUpload技术实现了一个 MultipartResolver 实现类:CommonsMultipartResolver

这里的enctype设置一般有如下几个选项:

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
  • text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。

接下来就实践下文件上传和下载

1 注册multipartResolver解析器

首先我们需要在Bean注册文件上传的解析器,这样SpringMVC框架才能直接使用

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="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/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
    <context:component-scan base-package="com.example.file.controller"/>
    <!-- 让Spring MVC不处理静态资源 -->
    <mvc:default-servlet-handler />
    <!--
    支持mvc注解驱动
        在spring中一般采用@RequestMapping注解来完成映射关系
        要想使@RequestMapping注解生效
        必须向上下文中注册DefaultAnnotationHandlerMapping
        和一个AnnotationMethodHandlerAdapter实例
        这两个实例分别在类级别和方法级别处理。
        而annotation-driven配置帮助我们自动完成上述两个实例的注入。
     -->
    <mvc:annotation-driven />
    <!--视图解析器-->
    <!--通过视图解析器解析处理器返回的逻辑视图名并传递给DispatcherServlet-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>
    <mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    <!-- 配置文件上传的解析器:-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 上传文件大小上限,单位为字节(10MB) -->
        <property name="maxInMemorySize">
            <value>10485760</value>
        </property>
        <!-- 设定默认编码 等 -->
        <property name="defaultEncoding" value="UTF-8"></property>
    </bean>
</beans>

2 编写前端JSP页面

前端我们需要编写一个上传的页面,用来上传文件

<%--
  Created by IntelliJ IDEA.
  User: 13304
  Date: 2021/9/12
  Time: 22:17
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="file/upload" enctype="multipart/form-data" method="post">
    <input type="file" name="file"/>
    <input type="submit" value="upload">
</form>
</body>
</html>

3 编写后端Controller

我们主要使用CommonsMultipartFile对象来处理文件对象,进行相关的操作。MultipartFile 封装了请求数据中的文件,此时这个文件存储在内存中或临时的磁盘文件中,需要将其转存到一个合适的位置,因为请求结束后临时存储将被清空

package com.example.file.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
@RestController
@RequestMapping("/file")
public class FileController {
    //@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象
    @RequestMapping("/upload")
    public String fileUpload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
        // 判断文件是否为空,空则返回失败页面
        if (file.isEmpty()) {
            return "文件为空,无法上传";
        }
        // 获取文件存储路径(绝对路径)
        String path = request.getServletContext().getRealPath("/WEB-INF/file");
        // 获取原文件名
        String fileName = file.getOriginalFilename();
        // 创建文件实例
        File filePath = new File(path, fileName);
        // 如果文件目录不存在,创建目录
        if (!filePath.getParentFile().exists()) {
            filePath.getParentFile().mkdirs();
            System.out.println("创建目录" + filePath);
        }
        // 写入文件
        file.transferTo(filePath);
        return "文件上传成功";
    }
}

4 文件上传测试

我们上传文件进行测试,在jsp页面上上传文件:

然后在服务器目录上就可以看到该文件:

文件下载

文件下载比较简单,SpringMVC提供了一个 ResponseEntity 类型,使用它可以很方便地定义返回的 HttpHeaders 和HttpStatus

1 编写前端JSP页面

前端我们需要编写一个JSP页面用来输入要下载的文件名并且下载

<%--
  Created by IntelliJ IDEA.
  User: tianmaolin
  Date: 2021/9/13
  Time: 8:35 下午
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="file/download" enctype="multipart/form-data" method="post">
    <input type="text" name="fileName"/>
    <input type="submit" value="download">
</form>
</body>
</html>

2 编写后端Controller

后端我们通常使用ResponseEntity对象来处理返回的文件:

@RequestMapping(value="/download")
    public ResponseEntity<byte[]> download(HttpServletRequest request, @RequestParam("fileName") String fileName) throws Exception{
        //下载文件路径
        String path = request.getServletContext().getRealPath("/WEB-INF/file");
        File filePath = new File(path, fileName);
        //设置请求头
        HttpHeaders headers = new HttpHeaders();
        //下载显示的文件名,并解决中文名称乱码问题
        String downloadFileName = new String(fileName.getBytes("UTF-8"),"iso-8859-1");
        //通知浏览器以attachment(下载方式)打开
        headers.setContentDispositionFormData("attachment", downloadFileName);
        //applicatin/octet-stream: 二进制流数据(最常见的文件下载)
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(filePath), headers, HttpStatus.CREATED);
    }

3 文件下载测试

然后我们在浏览器中请求文件下载地址,并输入我们刚上传的文件名进行下载:

总结一下

文件上传和下载都还是比较简单的,但是如果不借助SpringMVC,而是通过Servlet去实现则还需要处理复杂的字节流或者字符流,有了框架以及框架提供给我们的对象,可以方便的进行各种操作,例如转存文件,原样返回给浏览器服务器文件。学到这里SSM框架就学习完了,相当于打好了基础,接下来就是上主菜Spring Boot了。

相关文章
|
5月前
|
JSON 前端开发 Java
spring mvc Rest风格
spring mvc Rest风格
39 0
|
4月前
|
前端开发 Java 应用服务中间件
我以为我对Spring MVC很了解,直到我遇到了...
所有人都知道Spring MVC是是开发的,却鲜有人知道Spring MVC的理论基础来自于1978 年提出MVC模式的一个老头子,他就是Trygve Mikkjel Heyerdahl Reenskaug,挪威计算机科学家,名誉教授。Trygve Reenskaug的MVC架构思想早期用于图形用户界面(GUI) 的软件设计,他对MVC是这样解释的。MVC 被认为是解决用户控制大型复杂数据集问题的通用解决方案。最困难的部分是为不同的架构组件想出好的名字。模型-视图-编辑器是第一个。
123 1
我以为我对Spring MVC很了解,直到我遇到了...
|
5月前
|
Web App开发 前端开发 Java
基于Spring3 MVC实现基于HTML form表单文件上传
基于Spring3 MVC实现基于HTML form表单文件上传
44 7
基于Spring3 MVC实现基于HTML form表单文件上传
|
4月前
|
前端开发 Java Spring
Spring MVC中使用ModelAndView传递数据
Spring MVC中使用ModelAndView传递数据
|
5月前
|
JSON 前端开发 Java
【JavaEE进阶】 关于Spring MVC 响应
【JavaEE进阶】 关于Spring MVC 响应
54 3
|
5月前
|
设计模式 前端开发 Java
【Spring MVC】快速学习使用Spring MVC的注解及三层架构
【Spring MVC】快速学习使用Spring MVC的注解及三层架构
71 1
|
5月前
|
前端开发 Dubbo Java
spring面试题_spring mvc面试题_springboot面试题库
spring面试题_spring mvc面试题_springboot面试题库
|
5月前
|
缓存 NoSQL Java
在 SSM 架构(Spring + SpringMVC + MyBatis)中,可以通过 Spring 的注解式缓存来实现 Redis 缓存功能
【6月更文挑战第18天】在SSM(Spring+SpringMVC+MyBatis)中集成Redis缓存,涉及以下步骤:添加Spring Boot的`spring-boot-starter-data-redis`依赖;配置Redis连接池(如JedisPoolConfig)和连接工厂;在Service层使用`@Cacheable`注解标记缓存方法,指定缓存名和键生成策略;最后,在主配置类启用缓存注解。通过这些步骤,可以利用Spring的注解实现Redis缓存。
74 2
|
5月前
|
前端开发 Java 应用服务中间件
Spring框架第六章(SpringMVC概括及基于JDK21与Tomcat10创建SpringMVC程序)
Spring框架第六章(SpringMVC概括及基于JDK21与Tomcat10创建SpringMVC程序)
|
4月前
|
XML 前端开发 Java
Spring Boot与Spring MVC的区别和联系
Spring Boot与Spring MVC的区别和联系