【SpringMVC】7—文件上传

⭐⭐⭐⭐⭐⭐
Github主页👉https://github.com/A-BigTree
笔记链接👉https://github.com/A-BigTree/Code_Learning
⭐⭐⭐⭐⭐⭐

如果可以,麻烦各位看官顺手点个star~😊

如果文章对你有所帮助,可以点赞👍收藏⭐支持一下博主~😆


文章目录

  • 7 文件上传
    • 7.1 表单
    • 7.2 SpringMVC环境
      • 7.2.1 依赖
      • 7.2.2 配置
    • 7.3 处理方法接受数据
    • 7.4 上传多个文件
      • 7.4.1 请求参数名不同
        • 表单
        • 处理方法
      • 7.4.2 请求参数相同
        • 表单
        • 处理方法
    • 7.5 文件转存
      • 7.5.1 底层机制
      • 7.5.2 本地转存
        • 实现方法
        • 缺陷
      • 7.5.3 文件服务器(采纳)
        • 总体机制
        • 好处
      • 7.5.4 文件服务器类型
        • 上传到其他模块

7 文件上传

7.1 表单

  • 第一点:请求方式必须是POST
  • 第二点:请求体的编码方式必须是multipart/form-data(通过form 标签的enctype属性设置);
  • 第三点:使用input标签、type属性设置为file来生成文件上传框;

如果没有将enctype属性设置为multipart/form-data,则运行时会抛出异常。

7.2 SpringMVC环境

7.2.1 依赖

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

7.2.2 配置

在SpringMVC的配置文件中加入multipart类型数据的解析器:

<bean id="multipartResolver" 
      class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    
    <!-- 由于上传文件的表单请求体编码方式是 multipart/form-data 格式,所以要在解析器中指定字符集 -->
    <property name="defaultEncoding" value="UTF-8"/>
    
</bean>

CommonsMultipartResolver的bean的id,必须是:multipartResolver 如果不是这个值,会在上传文件时报错。

7.3 处理方法接受数据

@RequestMapping("/simple/upload")
public String doUpload(
 
        // 表单提交的数据仍然是请求参数,所以使用 @RequestParam 注解接收
        @RequestParam("nickName") String nickName,
 
        // 对于上传的文件使用 MultipartFile 类型接收其相关数据
        @RequestParam("picture") MultipartFile picture
        ) throws IOException {
 
    String inputName = picture.getName();
    logger.debug("文件上传表单项的 name 属性值:" + inputName);
 
    // 获取这个数据通常都是为了获取文件本身的扩展名
    String originalFilename = picture.getOriginalFilename();
    logger.debug("文件在用户本地原始的文件名:" + originalFilename);
 
    String contentType = picture.getContentType();
    logger.debug("文件的内容类型:" + contentType);
 
    boolean empty = picture.isEmpty();
    logger.debug("文件是否为空:" + empty);
 
    long size = picture.getSize();
    logger.debug("文件大小:" + size);
 
    byte[] bytes = picture.getBytes();
    logger.debug("文件二进制数据的字节数组:" + Arrays.asList(bytes));
 
    InputStream inputStream = picture.getInputStream();
    logger.debug("读取文件数据的输入流对象:" + inputStream);
 
    Resource resource = picture.getResource();
    logger.debug("代表当前 MultiPartFile 对象的资源对象" + resource);
 
    return "target";
}

7.4 上传多个文件

7.4.1 请求参数名不同

表单

<form th:action="@{/save/head/picture}" method="post" enctype="multipart/form-data">
    昵称:<input type="text" name="nickName" value="龙猫" /><br/>
    头像:<input type="file" name="headPicture" /><br/>
    背景:<input type="file" name="backgroundPicture" /><br/>
    <button type="submit">保存</button>
</form>

处理方法

@RequestMapping("/save/head/picture")
public String saveHeadPicture(
        @RequestParam("nickName") String nickName,

        // MultipartFile 是专门接收上传文件的类型
        // 浏览器端的表单用一个名字携带一个文件:使用单个 MultipartFile 类型变量接收
        @RequestParam("headPicture") MultipartFile headPicture,

        // 如果有另外一个名字携带了另外一个文件,那就用另外一个 MultipartFile 接收
        @RequestParam("backgroundPicture") MultipartFile backgroundPicture
        ) throws IOException {

    log.debug("[普通表单项] nickName = " + nickName);
    log.debug("[文件表单项] 请求参数名 = " + headPicture.getName());
    log.debug("[文件表单项] 原始文件名 = " + headPicture.getOriginalFilename());
    log.debug("[文件表单项] 判断当前上传文件是否为空 = " + (headPicture.isEmpty()?"空":"非空"));
    log.debug("[文件表单项] 当前上传文件的大小 = " + headPicture.getSize());
    log.debug("[文件表单项] 当前上传文件的二进制内容组成的字节数组 = " + headPicture.getBytes());
    log.debug("[文件表单项] 能够读取当前上传文件的输入流 = " + headPicture.getInputStream());

    log.debug("[另一个文件] 原始文件名 = " + backgroundPicture.getOriginalFilename());
    return "target";
}

7.4.2 请求参数相同

表单

<form th:action="@{/save/multi/file}" method="post" enctype="multipart/form-data">
    文件一:<input type="file" name="fileList" /><br/>
    文件二:<input type="file" name="fileList" /><br/>
    文件三:<input type="file" name="fileList" /><br/>
    <button type="submit">保存</button>
</form>

处理方法

@RequestMapping("/save/multi/file")
public String saveMultiFile(
    
        // 浏览器端的表单用一个名字携带多个文件:使用 List<MultipartFile> 类型变量接收
        @RequestParam("fileList") List<MultipartFile> fileList) {

    for (MultipartFile multipartFile : fileList) {
        String originalFilename = multipartFile.getOriginalFilename();
        logger.debug("originalFilename = " + originalFilename);
    }

    return "target";
}

7.5 文件转存

7.5.1 底层机制

在这里插入图片描述

7.5.2 本地转存

在这里插入图片描述

实现方法

  1. 创建保存文件的目录;

这个目录如果是空目录,那么服务器部署运行时很容易会忽略这个目录。为了避免这个问题,在这个目录下随便创建一个文件,随便写点内容即可。

  1. 编写转存代码;
……
 
// 1、准备好保存文件的目标目录
// ①File 对象要求目标路径是一个物理路径(在硬盘空间里能够直接找到文件的路径)
// ②项目在不同系统平台上运行,要求能够自动兼容、适配不同系统平台的路径格式
//      例如:Window系统平台的路径是 D:/aaa/bbb 格式
//      例如:Linux系统平台的路径是 /ttt/uuu/vvv 格式
//      所以我们需要根据『不会变的虚拟路径』作为基准动态获取『跨平台的物理路径』
// ③虚拟路径:浏览器通过 Tomcat 服务器访问 Web 应用中的资源时使用的路径
String destFileFolderVirtualPath = "/head-picture";
 
// ④调用 ServletContext 对象的方法将虚拟路径转换为真实物理路径
String destFileFolderRealPath = servletContext.getRealPath(destFileFolderVirtualPath);
 
// 2、生成保存文件的文件名
// ①为了避免同名的文件覆盖已有文件,不使用 originalFilename,所以需要我们生成文件名
// ②我们生成文件名包含两部分:文件名本身和扩展名
// ③声明变量生成文件名本身
String generatedFileName = UUID.randomUUID().toString().replace("-","");
 
// ④根据 originalFilename 获取文件的扩展名
String fileExtname = originalFilename.substring(originalFilename.lastIndexOf("."));
 
// ⑤拼装起来就是我们生成的整体文件名
String destFileName = generatedFileName + "" + fileExtname;
 
// 3、拼接保存文件的路径,由两部分组成
//      第一部分:文件所在目录
//      第二部分:文件名
String destFilePath = destFileFolderRealPath + "/" + destFileName;
 
// 4、创建 File 对象,对应文件具体保存的位置
File destFile = new File(destFilePath);
 
// 5、执行转存
picture.transferTo(destFile);
 
……

缺陷

  • Web 应用重新部署时通常都会清理旧的构建结果,此时用户以前上传的文件会被删除,导致数据丢;
  • 项目运行很长时间后,会导致上传的文件积累非常多,体积非常大,从而拖慢 Tomcat 运行速度;
  • 当服务器以集群模式运行时,文件上传到集群中的某一个实例,其他实例中没有这个文件,就会造成数据不一致;
  • 不支持动态扩容,一旦系统增加了新的硬盘或新的服务器实例,那么上传、下载时使用的路径都需要跟着变化,导致 Java 代码需要重新编写、重新编译,进而导致整个项目重新部署;

7.5.3 文件服务器(采纳)

总体机制

在这里插入图片描述

好处

  • 不受 Web 应用重新部署影响
  • 在应用服务器集群环境下不会导致数据不一致
  • 针对文件读写进行专门的优化,性能有保障
  • 能够实现动态扩容

在这里插入图片描述

7.5.4 文件服务器类型

  • 第三方平台:
    • 阿里的OSS对象存储服务;
    • 七牛云;
  • 自己搭建服务器:FastDFS等;

上传到其他模块

这种情况肯定出现在分布式架构中,常规业务功能不会这么做,采用这个方案的一定是特殊情况,这种情况极其少见。

在这里插入图片描述

MultipartFile接口中有一个对应方法:

/**
 * Return a Resource representation of this MultipartFile. This can be used
 * as input to the {@code RestTemplate} or the {@code WebClient} to expose
 * content length and the filename along with the InputStream.
 * @return this MultipartFile adapted to the Resource contract
 * @since 5.1
 */
default Resource getResource() {
  return new MultipartFileResource(this);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/10058.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

布隆过滤器讲解及基于Guava BloomFilter案例

目录 1、布隆过滤器是什么 2、主要作用 3、存储过程 4、查询过程 5、布隆过滤器的删除操作 6、优点 7、缺点 8、测试误判案例 8.1、引入Guava依赖 8.2、编写测试代码 8.3、测试 8.4、BloomFilter实现原理 9、总结 推荐博主视频&#xff0c;讲的很棒&#xff1a;布隆…

华为运动健康服务Health Kit 6.10.0版本新增功能速览!

华为运动健康服务&#xff08;HUAWEI Health Kit&#xff09;6.10.0 版本新增的能力有哪些&#xff1f; 阅读本文寻找答案&#xff0c;一起加入运动健康服务生态大家庭&#xff01; 一、 支持三方应用查询用户测量的连续血糖数据 符合申请Health Kit服务中开发者申请资质要求…

大数据项目之电商数据仓库系统回顾

文章目录一、实训课题二、实训目的三、操作环境四、 实训过程&#xff08;实训内容及主要模块&#xff09;五、实训中用到的课程知识点六、实训中遇到的问题及解决方法七、课程实训体会与心得八、程序清单一、实训课题 大数据项目之电商数据仓库系统 二、实训目的 完成一个电…

7.基于概率距离快速削减法的风光场景生成与削减方法

matlab代码&#xff1a;基于概率距离快速削减法的风光场景生成与削减方法 采用蒙特卡洛进行场景生成&#xff0c;并再次进行场景缩减。 clear;clc; %风电出力预测均值E W[5.8,6.7,5.8,5.1,6.3,5,6.2,6,4.1,6,7,6.8,6.5,6.9,5,5.6,6,5.8,6.2,4.7,3.3,4.4,5.6,5]; %取标准差为风…

在unreal中的基于波叠加的波浪水面材质原理和制作

关于水的渲染模型 如何渲染出真实的水体和模拟&#xff0c;是图形学&#xff0c;游戏开发乃至仿真领域很有意思的一件事 记得小时候玩《Command & Conquer: Red Alert 3》&#xff0c;被当时的水面效果深深震撼&#xff0c;作为一款2008年出的游戏&#xff0c;现在想起它…

算法:将一个数组旋转k步

题目 输入一个数组如 [1,2,3,4,5,6,7]&#xff0c;输出旋转 k 步后的数组。 旋转 1 步&#xff1a;就是把尾部的 7 放在数组头部前面&#xff0c;也就是 [7,1,2,3,4,5,6]旋转 2 步&#xff1a;就是把尾部的 6 放在数组头部前面&#xff0c;也就是 [6,7,1,2,3,4,5]… 思路 思…

C++继承(上)

一、继承的概念及定义1.继承的概念2.继承定义2.1定义格式2.2继承关系和访问限定符2.3继承基类成员访问方式的变化二、基类和派生类对象赋值转换三、继承中的作用域一、继承的概念及定义 1.继承的概念 继承机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允…

聊聊如何运用JAVA注解处理器(APT)

什么是APT APT&#xff08;Annotation Processing Tool&#xff09;它是Java编译期注解处理器&#xff0c;它可以让开发人员在编译期对注解进行处理&#xff0c;通过APT可以获取到注解和被注解对象的相关信息&#xff0c;并根据这些信息在编译期按我们的需求生成java代码模板或…

【SQL Server】数据库开发指南(一)数据库设计

文章目录一、数据库设计的必要性二、什么是数据库设计三、数据库设计的重要性五、数据模型5.1 实体-关系&#xff08;E-R&#xff09;数据模型5.2 实体&#xff08;Entity&#xff09;5.3 属性&#xff08;Attribute&#xff09;5.5 关系&#xff08;Relationship&#xff09;六…

和ChatGPT-4聊完后,我觉得一切可能已经来不及了

了然无味&#xff0c;晴空万里&#xff01;和ChatGPT-4开始了一场坦诚的沟通&#xff0c;它全程都表现出高情商&#xff0c;以及不断尽量安抚我的情绪&#xff0c;而这&#xff0c;恰恰令我脊背发凉。 部分文字截取 ZM&#xff1a;我能不能理解每次对话就是一次你的“生命” G&…

LeetCode刷题6:二叉树篇之第 1 节

提示1&#xff1a;本篇先带大家了解二叉树的基础理论&#xff0c;后给出4道基础题目&#xff0c;不难&#xff0c;冲啊~ 算法刷题系列 LeetCode刷题1&#xff1a;数组篇LeetCode刷题2&#xff1a;链表篇LeetCode刷题3&#xff1a;哈希篇LeetCode刷题4&#xff1a;字符串篇Lee…

1678_计算机架构黄金时代_文章阅读

全部学习汇总&#xff1a; GreyZhang/g_risc_v: Learning notes about RISC V. (github.com) 看了一份几年前的文章&#xff0c;觉得还是挺有收获的&#xff0c;因此做一个简单的整理。 对于架构有很大影响的主要考虑四点&#xff1a;专用硬件的实现、高安全性的要求、开放指令…

【Pandas】① Pandas 数据处理基础

介绍 Pandas 是非常著名的开源数据处理库&#xff0c;我们可以通过它完成对数据集进行快速读取、转换、过滤、分析等一系列操作。除此之外&#xff0c;Pandas 拥有强大的缺失数据处理与数据透视功能&#xff0c;可谓是数据预处理中的必备利器。 知识点 数据类型数据读取数据选择…

有效的括号(力扣刷题)代码随想录刷题

给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个右括号都有一个对应的相同类型的左…

RK3568平台开发系列讲解(驱动基础篇)mmap系统调用详解

🚀返回专栏总目录 文章目录 一、什么是mmap二、mmap映射类型2.1、私有匿名映射2.2、私有文件映射2.3、共享文件映射2.4、共享匿名映射沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本文将详细介绍mmap系统调用。 一、什么是mmap mmap/munmap函数是用户空间中常用的…

Nacos 性能报告

目录 一、测试目的 二、测试工具 三、测试环境 1. 环境 服务端 客户端 2. 启动参数 服务端 客户端 四、测试场景 1. 大规模服务注册后达到稳定状态 场景描述 2. 大规模服务注册达到稳定状态后&#xff0c;部分实例频繁发布 场景描述 五、测试数据 1. 大规模服务…

软件测试基础

软件测试的定义、软件测试的目的 IEEE&#xff1a;The process of running or testing the system manually or automatically by using tools, in order to verify whether it satisfies the requirements or to make clear the differences between the actual outcome and…

DDoS攻击实验笔记

DoS&DDoS简介 DoS(Denial of Service)&#xff0c;拒绝服务攻击是通过一些方法影响服务的可用性&#xff0c;比如早期主要基于系统和应用程序的漏洞&#xff0c;只需要几个请求或数据包就能导致长时间的服务不可用&#xff0c;但易被入侵检测系统发现。 DDoS(Distributed D…

日撸 Java 三百行day28-30

文章目录说明day28-30 Huffman 编码 (节点定义与文件读取)1.建树过程&#xff08;以图为例&#xff09;2.哈夫曼树特点3.分析代码过程3.1 抽象成员变量3.2结合文章梳理思路1.读文本2.解析文本内容&#xff1a;3.建树4.生成哈夫曼编码5.编码6.解码4.其他4.1 java 类型强转4.2 ja…

linux线程调度策略

系统中既有分时调度&#xff0c;又有时间片轮转调度和先进先出调度 学习这个主要为了在linux多线程中&#xff0c;解决几条指令间延时在1-2ms内&#xff1b; 1.比如之前处理过&#xff1a;给一个板子发送一个can指令&#xff0c;接着需要给另外一个模块发送移动指令&#xff0c…