前言
1. 文件上传
在我们完成的新增员工功能中,还存在一个问题:没有头像(图片缺失)

上述问题,需要我们通过文件上传技术来解决。下面我们就进入到文件上传技术的学习。
文件上传技术这块我们主要讲解三个方面:首先我们先对文件上传做一个整体的介绍,接着再学习文件上传的本地存储方式,最后学习云存储方式。
接下来我们就先来学习下什么是文件上传。
1.1 简介
文件上传,是指将本地图片、视频、音频等文件上传到服务器,供其他用户浏览或下载的过程。
文件上传在项目中应用非常广泛,我们经常发微博、发微信朋友圈都用到了文件上传功能。

在我们的案例中,在新增员工的时候,要上传员工的头像,此时就会涉及到文件上传的功能。在进行文件上传时,我们点击加号或者是点击图片,就可以选择手机或者是电脑本地的图片文件了。当我们选择了某一个图片文件之后,这个文件就会上传到服务器,从而完成文件上传的操作。
想要完成文件上传这个功能需要涉及到两个部分:
- 前端程序
- 服务端程序
我们先来看看在前端程序中要完成哪些代码:
<form action="/upload" method="post" enctype="multipart/form-data">
姓名: <input type="text" name="username"><br>
年龄: <input type="text" name="age"><br>
头像: <input type="file" name="image"><br>
<input type="submit" value="提交">
</form>
上传文件的原始form表单,要求表单必须具备以下三点(上传文件页面三要素):
-
表单必须有file域,用于选择要上传的文件
<input type="file" name="image"/> -
表单提交方式必须为POST
通常上传的文件会比较大,所以需要使用 POST 提交方式
-
表单的编码类型enctype必须要设置为:multipart/form-data
普通默认的编码格式是不适合传输大型的二进制数据的,所以在文件上传时,表单的编码格式必须设置为multipart/form-data
前端页面的3要素我们了解后,接下来我们就来验证下所讲解的文件上传3要素。
在提供的"课程资料"中有一个名叫"文件上传"的文件夹,直接将里的"upload.html"文件,复制到springboot项目工程下的static目录里面。

下面我们来验证:删除form表单中enctype属性值,会是什么情况?
- 在IDEA中直接使用浏览器打开upload.html页面

- 选择要上传的本地文件

- 点击"提交"按钮,进入到开发者模式观察

我们再来验证:设置form表单中enctype属性值为multipart/form-data,会是什么情况?
<form action="/upload" method="post" enctype="multipart/form-data">
姓名: <input type="text" name="username"><br>
年龄: <input type="text" name="age"><br>
头像: <input type="file" name="image"><br>
<input type="submit" value="提交">
</form>

知道了前端程序中需要设置上传文件页面三要素,那我们的后端程序又是如何实现的呢?
-
首先在服务端定义这么一个controller,用来进行文件上传,然后在controller当中定义一个方法来处理
/upload请求 -
在定义的方法中接收提交过来的数据 (方法中的形参名和请求参数的名字保持一致)
- 用户名:String name
- 年龄: Integer age
- 文件: MultipartFile image
Spring中提供了一个API:MultipartFile,使用这个API就可以来接收到上传的文件

问题:如果表单项的名字和方法中形参名不一致,该怎么办?
public Result upload(String username, Integer age, MultipartFile file) //file形参名和请求参数名image不一致解决:使用@RequestParam注解进行参数绑定
public Result upload(String username, Integer age, @RequestParam("image") MultipartFile file)
UploadController代码:
@Slf4j
@RestController
public class UploadController {
@PostMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image) {
log.info("文件上传:{},{},{}",username,age,image);
return Result.success();
}
}
后端程序编写完成之后,打个断点,以debug方式启动SpringBoot项目

打开浏览器输入:http://localhost:8080/upload.html , 录入数据并提交

通过后端程序控制台可以看到,上传的文件是存放在一个临时目录

打开临时目录可以看到以下内容:

表单提交的三项数据(姓名、年龄、文件),分别存储在不同的临时文件中:

当我们程序运行完毕之后,这个临时文件会自动删除。
所以,我们如果想要实现文件上传,需要将这个临时文件,要转存到我们的磁盘目录中。
1.2 本地存储
- 本地存储:在服务端,接收到上传上来的文件之后,将文件存储在本地服务器磁盘中。
前面我们已分析了文件上传功能前端和后端的基础代码实现,文件上传时在服务端会产生一个临时文件,请求响应完成之后,这个临时文件被自动删除,并没有进行保存。下面呢,我们就需要完成将上传的文件保存在服务器的本地磁盘上。
1.2.1 使用MultipartFile类提供的API方法来存储上传的文件
代码实现:
- 在服务器本地磁盘上创建images目录,用来存储上传的文件(例:E盘创建images目录)
- 使用MultipartFile类提供的API方法,把临时文件转存到本地磁盘目录下
MultipartFile 常见方法:
- String getOriginalFilename(); //获取原始文件名
- void transferTo(File dest); //将接收的文件转存到磁盘文件中
- long getSize(); //获取文件的大小,单位:字节
- byte[] getBytes(); //获取文件内容的字节数组
- InputStream getInputStream(); //获取接收到的文件内容的输入流
@Slf4j
@RestController
public class UploadController {
@PostMapping("/upload")
public AjaxResult upload(String username, Integer age, MultipartFile image) throws IOException {
log.info("文件上传:{},{},{}",username,age,image);
//获取原始文件名
String originalFilename = image.getOriginalFilename();
//将文件存储在服务器的磁盘目录(由于这里是我在本地的win机子上启动的服务,所以这里就直接放E盘了;如果你是已经部署到了服务器上,在服务器上启动的服务,那这里路径换成一个服务器上你想要放的路径即可)
image.transferTo(new File("E:/images/"+originalFilename));
return AjaxResult.success();
}
}
利用postman测试:
注意:请求参数名和controller方法形参名保持一致
1.2.2 使用UUID替换原本文件名解决上传文件名相同会覆盖原文件问题
通过postman测试,我们发现文件上传是没有问题的。但是由于我们是使用原始文件名作为所上传文件的存储名字,当我们再次上传一个名为1.jpg文件时,发现会把之前已经上传成功的文件覆盖掉。
解决方案:保证每次上传文件时文件名都唯一的(使用UUID获取随机文件名)
@Slf4j
@RestController
public class UploadController {
@PostMapping("/upload")
public AjaxResult upload(String username, Integer age, MultipartFile image) throws IOException {
log.info("文件上传:{},{},{}",username,age,image);
//获取原始文件名
String originalFilename = image.getOriginalFilename();
//构建新的文件名
String extname = originalFilename.substring(originalFilename.lastIndexOf("."));//文件扩展名
String newFileName = UUID.randomUUID().toString()+extname;//随机名+文件扩展名
//将文件存储在服务器的磁盘目录
image.transferTo(new File("E:/images/"+newFileName));
return AjaxResult.success();
}
}


1.2.3 解决在SpringBoot中,文件上传时默认单个文件最大大小为1M问题(配置文件配置最大上传大小)
在解决了文件名唯一性的问题后,我们再次上传一个较大的文件(超出1M)时发现,后端程序报错:

报错原因呢是因为:在SpringBoot中,文件上传时默认单个文件最大大小为1M
那么如果需要上传大文件,可以在application.properties进行如下配置:
#配置单个文件最大上传大小
spring.servlet.multipart.max-file-size=10MB
#配置单个请求最大上传大小(一次请求可以上传多个文件)
spring.servlet.multipart.max-request-size=100MB
1.2.4 使用MultipartFile[]数组一次上传多个文件
@Slf4j
@RestController
public class UploadController {
@PostMapping("/upload")
public AjaxResult upload(
@RequestParam("images") MultipartFile[] images // 改为接收文件数组
) throws IOException {
log.info("文件上传:用户={}, 年龄={}, 文件数={}", images.length);
// 遍历所有文件
for (MultipartFile image : images) {
// 跳过空文件
if (image.isEmpty()) {
continue;
}
// 获取原始文件名
String originalFilename = image.getOriginalFilename();
// 构建新文件名
String extname = originalFilename.substring(originalFilename.lastIndexOf("."));
String newFileName = UUID.randomUUID().toString() + extname;
// 保存文件到磁盘
image.transferTo(new File("E:/images/" + newFileName));
log.info("已保存文件:{} -> {}", originalFilename, newFileName);
}
return AjaxResult.success("成功上传 " + images.length + " 个文件");
}
}


1.2.4 本地存储的缺陷
到时此,我们文件上传的本地存储方式已完成了。但是这种本地存储方式还存在一问题:

如果直接存储在服务器的磁盘目录中,存在以下缺点:
- 不安全:磁盘如果损坏,所有的文件就会丢失
- 容量有限:如果存储大量的图片,磁盘空间有限(磁盘不可能无限制扩容)
- 无法直接访问(硬伤,一般图片我们想要直接url就能浏览器访问的)
为了解决上述问题呢,通常有两种解决方案:
- 自己搭建存储服务器,如:fastDFS 、MinIO
- 使用现成的云服务,如:阿里云,腾讯云,华为云,亚马逊S3云服务Amazon S3 (Simple Storage Service)等等
1.3 阿里云OSS(阿里云对象存储服务)
1.3.1 准备
1.3.1.1 创建桶
阿里云是阿里巴巴集团旗下全球领先的云计算公司,也是国内最大的云服务提供商 。

云服务指的就是通过互联网对外提供的各种各样的服务,比如像:语音服务、短信服务、邮件服务、视频直播服务、文字识别服务、对象存储服务等等。
当我们在项目开发时需要用到某个或某些服务,就不需要自己来开发了,可以直接使用阿里云提供好的这些现成服务就可以了。比如:在项目开发当中,我们要实现一个短信发送的功能,如果我们项目组自己实现,将会非常繁琐,因为你需要和各个运营商进行对接。而此时阿里云完成了和三大运营商对接,并对外提供了一个短信服务。我们项目组只需要调用阿里云提供的短信服务,就可以很方便的来发送短信了。这样就降低了我们项目的开发难度,同时也提高了项目的开发效率。(大白话:别人帮我们实现好了功能,我们只要调用即可)
云服务提供商给我们提供的软件服务通常是需要收取一部分费用的



最低0.47元/天 解锁文章
1万+

被折叠的 条评论
为什么被折叠?



