Spring MVC (文件上传下载)

发布时间 2023-12-27 22:20:45作者: 学Java的`Bei

 

1. 介绍

  1)三层架构:

    表现层(UI):负责数据展示;
    业务层(BLL):负责业务处理;
    数据层(DAL):负责数据操作;

  2)MVC

 

    SpringMVC(Model-View-Controller)是Spring的一部分,基于Java的Web框架,用于开发Web应用框架。

    提供一种模型(Model)-视图(View)-控制器(Controller)的架构模式,用于构建灵活,可维护性和可扩展性的Web应用。

 

 

    •  Model(模型):数据模型,用于封装数据;

    •  View(视图):页面视图,用于展示数据;

    •  Controller(Handle 处理器):处理用户交互的调度器,用于根据用户需求处理程序逻辑;

 

 

    SpringBoot只是提供了一种更快更优的Spring方式,并不是Spring的增强版。
    Spring约定:简化配置。

2. 创建SpringMVC 步骤(上传文件与下载文件)

  1)后端:新建MavenWeb项目

   

 

   如下:

   

  2)后端:导入Maven依赖(pom.xml)

    <dependencies>
        <!-- Servlet 坐标 -->
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>5.0.0</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>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>

        <!-- servlet3.1规范的坐标 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!--jsp坐标-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <!--spring的坐标-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--spring web的坐标-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--springmvc的坐标-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>


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

        <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.2</version>
            </plugin>
        </plugins>
    </build>

  注:导入后背景有黄色高亮的是有漏洞的,但不影响;

 

  3)后端:创建controller层 -- > FileController类

  文件上传:

 1     // 文件上传
 2     //  @RequestMapping(value = "/upload", method = RequestMethod.POST)  等价于 @PostMapping()
 3     @PostMapping(value = "upload")
 4     @ResponseBody
 5     public String fileUpload(MultipartFile[] uploadFile) {
 6         for (MultipartFile file : uploadFile) {
 7             //获取原始文件名
 8             String originalFilename = file.getOriginalFilename();
 9 
10 
11             try {
12                 //  生成UUID
13                 //  修正文件名出现乱码 防止文件名重复造成覆盖
14                 originalFilename = new String(originalFilename.getBytes("ISO-8859-1"), "UTF-8");
15             } catch (UnsupportedEncodingException e) {
16                 throw new RuntimeException(e);
17             }
18             System.out.println("文件名:" + originalFilename);
19             // UUID随机
20             String newFile = UUID.randomUUID().toString() + "_" + originalFilename;
21 
22             // 指定文件上传路径
23             // Windows版--方式
24             String serverWin = "E:\\Chrom\\Download\\pictures\\";
25             
26             //  Linux版--方式
27             //  String serverLinux = "/home";
28 
29             File fileWin = new File(serverWin);
30             // 文件夹是否存在
31             if (!fileWin.exists()) {
32                 fileWin.mkdir();
33             }
34             try {
35                 // 文件与文件夹
36                 file.transferTo(new File(serverWin + newFile));
37             } catch (IOException e) {
38                 throw new RuntimeException(e);
39             }
40         }
41         return "success";
42     }

   

  文件下载:

    // 文件下载
    @GetMapping(value = "download")
    public ResponseEntity<byte[]> download(HttpServletRequest httpServletRequest, String filename) {
        // 指定文件下载目录
        // 文件在服务器的目录
        String fileDownloadWin = "E:\\Chrom\\Download\\pictures\\";

        // 指定文件下载名称
        File file = new File(fileDownloadWin + File.separator + filename);
        HttpHeaders httpHeaders = new HttpHeaders();
        filename = getFilename(httpServletRequest, filename);
        httpHeaders.setContentDispositionFormData("attachment", filename);
        httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        try {
            return new ResponseEntity<>(FileUtils.readFileToByteArray(file), httpHeaders, HttpStatus.OK);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

 

 

  工具类:(解决不同浏览器版本兼容问题)

    /**
     * 工具类 解决 不同浏览器的版本兼容
     * @param request
     * @param filename
     * @return
     * @throws UnsupportedEncodingException
     */
    private String getFilename(HttpServletRequest request,String filename){
        //判断是不是IE内核的关键字
        String[] IEBrowerKeyWords = {"MSIE","Trident","Edge"};
        String keywords = request.getHeader("User-Agent");
        for (String keyWord : IEBrowerKeyWords) {
            if(keywords.contains(keyWord)){  //判断是否为IE浏览器
                try {
                    return URLEncoder.encode(filename,"UTF-8");
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        // 其他浏览器编码格式ISO-8859-1
        try {
            return new String(filename.getBytes("UTF-8"),"ISO-8859-1");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

 

   @ResponseBody 注解:将方法的返回值通过适当的转换器转换为指定的格式之后,写入到 response 对象的 body 区,通常用来返回 JSON、XML 数据。

 

  4)后端:创建spring-mvc.xml配置文件(本质就是spring的配置件)

    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    </bean>

    <!--注解开发-->
    <context:component-scan base-package="com.bei"/>

 

  5)后端:web.xml中配置前端控制器

    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:spring-mvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

 

  6)后端:配置tomcat

   

     

 

  7)前端:使用JQuery编写上传方法

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件上传</title>
    <!-- 导入JQuery -->
    <script type="text/javascript" src="./jquery-1.11.3.min.js"></script>
</head>

<body>

    <!-- 请求路径 -->
    <form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
        上传文件:&nbsp;&nbsp;<input type="button" value="添加文件" onclick="add()" />
        <div id="file" style="margin-top: 10px;" value="文件上传区域">

        </div>
        <input id="submit" type="submit" value="上传" style="display: none;margin-top: 10px;" />
    </form>
</body>

<script>
    function add() {
        var innerdiv = "<div>";
            // name='uploadFile' 一定是 上传方法内的参
        innerdiv += "<input type='file' name='uploadFile'>" +
            "<input type='button' value='删除' onclick='remove(this)'>";
        innerdiv += "</div>";
        $("#file").append(innerdiv);
        $("#submit").css("display", "block");
    }
    function remove(obj) {
        $(obj).parent().remove();
        if ($("#file div").length == 0) {
            $("#submit").css("display", "none");
        }
    }
</script>

  

  8)前端:使用JQuery编写下载方法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>下载</title>
</head>
<body>
    <!-- 请求路径  ?  文件名 -->
    <a href="http://localhost:8080/downloadThree?fileName=小可莉.mp4">小可莉.mp4</a>
</body>
</html>

 

3. 工作流程分析

 

  1)服务器启动

 

    • 加载web.xml中DispatcherServlet;

    • 读取spring-mvc.xml中的配置,加载所有com.xx 包中所有标记为bean的类;

    • 读取bean中方法上方标注@RequestMapping的内容;

 

  2)处理请求

 

    • DispatcherServlet配置拦截所有请求 /  ;

    • 使用请求路径与所有加载的@RequestMapping的内容进行比对;

    • 执行对应的方法;

    • 根据方法的返回值在webapp目录中查找对应的页面并展示;

 

  3)web三大组件有 处理器映射,处理器适配器, 视图解析器

    •  dispatcherServlet 前置控制器,负责接收并处理所有的web请求,根据handlerMapping(处理器映射)找到具体的Controller(处理器),由controller完成具体的处理逻辑。
    •  HandlerMapping(处理器映射器):负责处理web请求和具体的Controller之间的映射关系匹配。
    • HandlerAdapter(处理器适配器) 通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。 主要处理方法参数、相关注解、数据绑                                                         定、消息转换、返回值、调用视图解析器等等。
    •  Controller(处理器):DispatherServlet的次级控制器,web请求的具体处理者。DispatherServlet获得handlerMapping的返回结果后,调用controller的处理方法处理当前的业务请求,处理完成后返                                                         回ModelAndView对象。
    •  ViewResolver( 视图解析器):用来处理视图名与具体的view实例之间的映射对应关系。根据ModelAndView中的视图名查找相应的View实现类,然后将查找的结果返回给DispatcherServlet,                                                                 DispatcherServlet最终会将ModelAndView中的模型数据交给返回的View处理最终的视图渲染工作。

 

 

  Springmvc架构原理解析:

      第一步:发起请求到前端控制器(DispatcherServlet);

      第二步:前端控制器请求HandlerMapping查找 Handler,可以根据xml配置、注解进行查找;

      第三步:处理器映射器HandlerMapping向前端控制器返回Handler;

      第四步:前端控制器调用处理器适配器去执行Handler;

      第五步:处理器适配器去执行Handler;

      第六步:Handler执行完成给适配器返回ModelAndView;

      第七步:处理器适配器向前端控制器返回ModelAndView ModelAndView是springmvc框架的一个底层对象,包括 Model和view;

      第八步:前端控制器请求视图解析器去进行视图解析 根据逻辑视图名解析成真正的视图(jsp);

      第九步:视图解析器向前端控制器返回View;

      第十步:前端控制器进行视图渲染,视图渲染将模型数据(在ModelAndView对象中)填充到request域;

      第十一步:前端控制器向用户响应结果。

 

 

4. 请求参数的绑定

    1)默认类型:

      直接放在参数上就可以使用的数据,HttpServletRequest;

    2)简单类型:

      直接将简单类型的数据放在方法里,如果前端参数和后端参数名字一样,自动匹配;

      名字不一样:@RequsetParam(“前端的值”) 就可以将前传的值和后端参数映射;

    3 对象 :

      前端的参数要和对象的属性名称必须、一定一致,会自动封装。

    4 对象嵌套:

      参数和对象的属性名称一致,前端参数对象子属性必须(子对象.属性);

    5 自定义数据的绑定:

      ① 编写转换器类,作用是将前端的数据类型转换成后端的数据类型,继承converter;

      ② 配置文件中,添加转化器驱动。

    6 数组:

      前端数组中是简单类型的数据,前端数组中的name要和后端数组名称一致;

    7 集合:

      后端接受的对象是含有List<对象>属性的,那么前端的name值格式要和后端list属性名称一致,而且用索引的格式 list[0].属性(list集合里对象的属性名称)。