0%

文件上传

文件上传,是指将本地图片、视频、音频等文件上传到服务器,供其他用户浏览或下载的过程。

文件上传在项目中应用非常广泛,我们经常发微博、发微信朋友圈都用到了文件上传功能。

实现文件上传

  • 首先在服务端定义这么一个controller,用来进行文件上传,然后在controller当中定义一个方法来处理/upload 请求

  • 在定义的方法中接收提交过来的数据 (方法中的形参名和请求参数的名字保持一致)

    • 用户名:String name
    • 年龄: Integer age
    • 文件: MultipartFile image

    Spring中提供了一个API:MultipartFile,使用这个API就可以来接收到上传的文件

    image-20231016230440267

问题:如果表单项的名字和方法中形参名不一致,该怎么办?

  • public Result upload(String username,
                         Integer age, 
                         MultipartFile file) //file形参名和请求参数名image不一致
    
    1
    2
    3
    4
    5
    6
    7

    解决:使用@RequestParam注解进行参数绑定

    - ~~~java
    public Result upload(String username,
    Integer age,
    @RequestParam("image") MultipartFile file)

UploadController代码:

1
2
3
4
5
6
7
8
9
10
11
@Slf4j
@RestController
public class UploadController {

@PostMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image) {
log.info("文件上传:{},{},{}",username,age,image);
return Result.success();
}

}

但是此时文件时存储在了我们的临时目录里,程序运行完毕之后,临时文件就会自动删除,我们怎么保存文件呢?

本地存储

前面我们已分析了文件上传功能前端和后端的基础代码实现,文件上传时在服务端会产生一个临时文件,请求响应完成之后,这个临时文件被自动删除,并没有进行保存。下面呢,我们就需要完成将上传的文件保存在服务器的本地磁盘上。

代码实现:

  1. 在服务器本地磁盘上创建images目录,用来存储上传的文件(例:E盘创建images目录)
  2. 使用MultipartFile类提供的API方法,把临时文件转存到本地磁盘目录下

MultipartFile 常见方法:

  • String getOriginalFilename(); //获取原始文件名
  • void transferTo(File dest); //将接收的文件转存到磁盘文件中
  • long getSize(); //获取文件的大小,单位:字节
  • byte[] getBytes(); //获取文件内容的字节数组
  • InputStream getInputStream(); //获取接收到的文件内容的输入流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Slf4j
@RestController
public class UploadController {

@PostMapping("/upload")
public Result 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 Result.success();
}

}

注意:在SpringBoot中,文件上传时默认单个文件最大大小为1M

那么如果需要上传大文件,可以在配置文件进行如下配置:

1
2
3
4
5
#配置单个文件最大上传大小
spring.servlet.multipart.max-file-size=10MB

#配置单个请求最大上传大小(一次请求可以上传多个文件)
spring.servlet.multipart.max-request-size=100MB

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

image-20231016230835778

如果直接存储在服务器的磁盘目录中,存在以下缺点:

  • 不安全:磁盘如果损坏,所有的文件就会丢失
  • 容量有限:如果存储大量的图片,磁盘空间有限(磁盘不可能无限制扩容)
  • 无法直接访问

为了解决上述问题呢,通常有两种解决方案:

  • 自己搭建存储服务器,如:fastDFS 、MinIO
  • 使用现成的云服务,如:阿里云,腾讯云,华为云

阿里云OSS

阿里云对象存储OSS(Object Storage Service),是一款海量、安全、低成本、高可靠的云存储服务。使用OSS,您可以通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种文件。

在我们使用了阿里云OSS对象存储服务之后,我们的项目当中如果涉及到文件上传这样的业务,在前端进行文件上传并请求到服务端时,在服务器本地磁盘当中就不需要再来存储文件了。我们直接将接收到的文件上传到oss,由 oss帮我们存储和管理,同时阿里云的oss存储服务还保障了我们所存储内容的安全可靠。

image-20231016230941159

那我们学习使用这类云服务,我们主要学习什么呢?其实我们主要学习的是如何在项目当中来使用云服务完成具体的业务功能。而无论使用什么样的云服务,阿里云也好,腾讯云、华为云也罢,在使用第三方的服务时,操作的思路都是一样的。

image-20221229093911113

SDK:Software Development Kit 的缩写,软件开发工具包,包括辅助软件开发的依赖(jar包)、代码示例等,都可以叫做SDK。

简单说,sdk中包含了我们使用第三方云服务时所需要的依赖,以及一些示例代码。我们可以参照sdk所提供的示例代码就可以完成入门程序。

第三方服务使用的通用思路,我们做一个简单介绍之后,接下来我们就来介绍一下我们当前要使用的阿里云oss对象存储服务具体的使用步骤。

image-20221229112451120

Bucket:存储空间是用户用于存储对象(Object,就是文件)的容器,所有的对象都必须隶属于某个存储空间。

下面我们根据之前介绍的使用步骤,完成准备工作:

  1. 注册阿里云账户(注册完成后需要实名认证)
  2. 注册完账号之后,就可以登录阿里云

image-20220904201839857

  1. 通过控制台找到对象存储OSS服务

image-20220904201932884

如果是第一次访问,还需要开通对象存储服务OSS

image-20220904202537579

image-20220904202618423

  1. 开通OSS服务之后,就可以进入到阿里云对象存储的控制台

image-20220904201810832

  1. 点击左侧的 “Bucket列表”,创建一个Bucket

image-20220904202235180

阿里云oss 对象存储服务的准备工作我们已经完成了,接下来我们就来完成第二步操作:参照官方所提供的sdk示例来编写入门程序。

首先我们需要来打开阿里云OSS的官方文档,在官方文档中找到 SDK 的示例代码:

image-20221229121848524

image-20221229122046597

如果是在实际开发当中,我们是需要从前往后仔细的去阅读这一份文档的,但是由于现在是教学,我们就只挑重点的去看。有兴趣的同学大家下来也可以自己去看一下这份官方文档。

image-20221229144342148

image-20221229160827124

参照官方提供的SDK,改造一下,即可实现文件上传功能:

实体类(存放阿里云的属性)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*阿里云OSS相关配置*/
@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliyunOSSProperties {
//区域
private String endpoint;
//身份ID
private String accessKeyId ;
//身份密钥
private String accessKeySecret ;
//存储空间
private String bucketName;
}

工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* 阿里云 OSS 工具类
*/
@Component
public class AliOSSUtils {
@Autowired
private AliyunOSSProperties aliyunOSSProperties;
/**
* 实现上传图片到OSS
*/
public String upload(MultipartFile file) throws IOException {
String endpoint = aliyunOSSProperties.getEndpoint();
String bucketName = aliyunOSSProperties.getBucketName();
String accessKeyId = aliyunOSSProperties.getAccessKeyId();
String accessKeySecret = aliyunOSSProperties.getAccessKeySecret();
// 获取上传的文件的输入流
InputStream inputStream = file.getInputStream();

// 避免文件覆盖
String originalFilename = file.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));

//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, inputStream);

//文件访问路径
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
// 关闭ossClient
ossClient.shutdown();
return url;// 把上传到oss的路径返回
}

}

在以上代码中,需要替换的内容为:

  • accessKeyId:阿里云账号AccessKey
  • accessKeySecret:阿里云账号AccessKey对应的秘钥
  • bucketName:Bucket名称
  • objectName:对象名称,在Bucket中存储的对象的名称
  • filePath:文件路径

AccessKey :

image-20221128020105943

运行以上程序后,会把本地的文件上传到阿里云OSS服务器上:

image-20221229161326919

集成代码

有了阿里云OSS存储后,我们可以将文件存储到阿里云OSS中

UploadController代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Slf4j
@RestController
public class UploadController {

@PostMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image) {
log.info("文件上传:{},{},{}",username,age,image);
//调用阿里云OSS工具类,将上传上来的文件存入阿里云
String url = aliOSSUtils.upload(image);
return Result.success(url);
}

}

将url返回给前端,前端访问进行渲染,同时将url存储到后端数据库