当前位置: 首页 > article >正文

【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);
}

http://www.kler.cn/a/10252.html

相关文章:

  • 智能电视/盒子的应用管理——通过ADB工具优化体验
  • POI实现根据PPTX模板渲染PPT
  • 使用pytest+openpyxl做接口自动化遇到的问题
  • 3.5【数据库系统】ER图
  • GitLab 如何跨版本升级?
  • RAFT: Recurrent All-Pairs Field Transforms for Optical Flow用于光流估计的循环全对场变换
  • 详细讲讲Java线程的状态
  • 林长制信息系统主要建设思路
  • Java实现图片缩放裁剪,图片像素比例变更,批量转换图片像素比
  • 遗传算法优化深度信念网络DBN的分类预测,GA-DBN分类预测
  • C++ 的fcntl函数
  • ChatGPT搭建语音智能助手
  • 工作中英语学习的几个阶段
  • Three.js教程:第一个3D场景
  • 【MyBatis Plus】003 -- 配置(基本、进阶、DB策略) 条件构造器
  • Linux下使用ClamAV病毒查杀
  • Lottie加载的一些坑
  • 【OpenCV-Python】cvui 之 trackbar
  • 因果推断14--DRNet论文和代码学习
  • 如果让你做技术负责人,你会怎么设计后端架构?
  • 查看 Elasticsearch 分析器
  • selenium库有哪些功能呢?都是如何实现的呢?
  • ( “树” 之 DFS) 543. 二叉树的直径 ——【Leetcode每日一题】
  • Git的安装与基本使用
  • 2021蓝桥杯真题大写 C语言/C++
  • 计算机网络笔记(横向)