SpringBoot + MinIO 轻松构建对象存储服务,支持私有化部署!
01、背景介绍
在实际的软件系统开发过程中,经常避免不了需要用到文件存储服务。
例如,对于小型的网站系统,通常会将文件存储服务和网站系统部署在一台服务器中,以实现低成本的资源投入,如果访问量不大,基本上没什么问题。当访问量逐渐升高,此时网站的文件资源读取越来越频繁,单台服务器可能难以承载较大的请求量,这个时候网站可能会出现打不开,甚至系统异常等问题。
当出现这个场景,很容易第一时间想到将文件采用云存储服务来解决。所谓云存储服务,简单的说,就是将访问很频繁的文件资源服务,由本地改成云厂商提供的文件存储服务,比如阿里云 OSS、七牛云、腾讯云、百度云等等,迁移之后,网站的访问压力会得到极大的释放,服务也会变得更加稳定。但是,这些云存储服务大部分都是收费的,以阿里云为例,数据存储通常按照 0.12 元/GB/月的标准来收费,虽然便宜,但是日积月累下来也是一笔不小的开支啊。
为了节省成本,很多项目团队会自己搭建一套云存储服务,比如采用开源的 fastDFS 工具来作为文件存储服务器,虽然能性能不错,但是软件安装环境非常复杂,最重要的是没有一个完整的技术文档,大部分都是某某公司或者某某网友自己总结的文档,每次维护起来很是麻烦。
直到出现了 MinIO,云存储服务工具又多了一个新的可选项。
MinIO 是一款号称世界上速度最快的对象存储服务器,专为大规模数据存储和分析而设计。它支持在各种环境中部署,包括物理服务器、虚拟机、容器等,最关键的是它的技术文档非常完善,非常容易上手;同时,对个人用户是完全开源免费的。
今天通过这篇文章,我们一起了解一下如何利用 MinIO 来搭建一套属于自己的云存储服务。
02、方案实践
2.1、minio 快速安装
minio 工具的安装非常简单,如果你本机安装了 Docker 容器,可以通过 Docker 命令一键实现安装操作。
以 windows 操作系统为例,安装命令如下。
docker run
-p 9000:9000
-p 9001:9001
--name minio1
-v D:miniodata:/data
-e "MINIO_ROOT_USER=ROOTUSER"
-e "MINIO_ROOT_PASSWORD=CHANGEME123"
quay.io/minio/minio server /data --console-address ":9001"
相关参数解读:
docker run
:表示启动运行容器-p
:表示为容器绑定一个本地的端口-name
:表示为容器创建一个本地的名字-v
:表示将文件路径设置为容器使用的持久卷位置。当 MinIO 将数据写入/data
时,该数据会镜像到本地路径~/minio/data
, 使其能够在容器重新启动时保持持久化。您可以设置任何具有读取、写入和删除权限的文件路径来使用。-e
:表示设置登陆控制台的用户名和密码。其中控制台的访问地址为http://本机ip:9001
,api 的访问地址为http://本机ip:9000
。
如果没有 docker 容器,可以采用软件包方式进行安装,具体实现方式可以参考官网文档,地址如下。
https://minio.org.cn/docs/minio/container/index.html
服务启动成功之后,在浏览器中访问http://127.0.0.1:9001
地址,会看到类似于如下界面。
输入上文设置的用户名和密码,即可登陆!
2.2、minio 使用介绍
登陆成功之后,会看到类似于如下的主界面。
由于官方并没有提供汉化版,如果想要实现中文展示,可以使用浏览器插件进行翻译,翻译之后的内容如下。
在对象存储服务里面,所有的文件都是以桶的形式来组织的。简单的说,你可以将桶看作是目录,这个目录下有很多的文件或者文件夹,这和其它云存储服务基本一致。
下面我们一起来快速体验一下!
2.2.1、创建存储桶
所有的文件必须要存储到桶中,因此我们需要先创建一个存储桶。
如果想要修改存储桶信息,点击左侧的Buckets
菜单,就可以看到相关的存储桶配置信息。
2.2.2、上传和下载文件
存储桶创建完成之后,就可以上传文件了。
点击Object Browser
菜单,可以看到刚刚创建的存储桶public-bucket
,点击进入,上传我们想要存储的文件了。
如果想要下载文件或者预览文件,点击文件,右侧会弹出相关的操作按钮,点击相应的操作按钮就可以了。
2.2.3、设置文件公开访问
默认创建的存储桶,都是私有桶,也就是说无法被公开访问。
以上文的文件为例,如果以 api 的方式直接访问,会提示无权限,示例如下:
通常来说,我们会将数据写入操作进行控制;对于读操作,很多不涉及安全问题的,我们希望能被互联网公开访问,以便加快文件的访问速度,此时如何实现呢?
可以在存储桶里面配置,将数据读取权限设置为公开访问,操作示例如下:
此时,我们再次以 api 的方式访问,结果如下:
可以清晰的看到,此时文件可以公开访问了。
2.3、springBoot 集成 minio 实现文件存储
最后,我们一起来看看,如何在 Spring Boot 工程中集成 minio 客户端以便实现文件存储服务。
2.3.1、创建用户访问密钥
MinIO 支持通过用户、密码来管理存储桶,我们可以利用 minio 客户端来实现文件的上传和下载。
点击Access Keys
菜单,创建用户名和密码并将其保存,下文会用到。
2.3.2、引入依赖包
在 Spring Boot 工程,引入 minio 客户端依赖包。
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.1.4</version>
</dependency>
2.3.3、添加相关配置
在application.properties
文件中,添加 minio 相关的配置信息.
minio.endpoint=http://127.0.0.1:9000
minio.access-key=o1TJJL9noE69KIgZtKQ0
minio.secret-key=KAi91ZUYHXCzCn1XUiHJ3qQflp50XFqlTCFt6Ik3
minio.bucket-name=public-bucket
2.3.4、编写 Minio 客户端配置类
基于上文的配置信息,编写 Minio 客户端配置类。
@Configuration
public class MinioConfig {
@Value("${minio.endpoint}")
private String minioEndpoint;
@Value("${minio.access-key}")
private String minioAccessKey;
@Value("${minio.secret-key}")
private String minioSecretKey;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(minioEndpoint)
.credentials(minioAccessKey, minioSecretKey)
.build();
}
}
2.3.5、编写上传和文件预览服务
接着利用 minioClient 客户端,编写上传和文件预览服务。
@RestController
public class FileController {
@Value("${minio.bucket-name}")
private String bucketName;
@Autowired
private MinioClient minioClient;
/**
* 文件上传
* @param file
* @return
* @throws IOException
*/
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) {
try {
ObjectWriteResponse response = minioClient.putObject(
PutObjectArgs
.builder()
.bucket(bucketName)
.object(file.getOriginalFilename())
.stream(file.getInputStream(), file.getInputStream().available(), -1)
.contentType(file.getContentType())
.build()
);
return "upload file success,tagId:" + response.etag();
} catch (Exception e) {
e.printStackTrace();
return "upload file error";
}
}
/**
* 构建预览地址
* @param fileName
* @return
* @throws Exception
*/
@GetMapping("/getPreviewUrl")
public String getPreviewUrl(@RequestParam("fileName") String fileName) throws Exception {
// 构建预览地址,默认15秒过期,无论是私有桶还是公有桶,文件通过链接都可以访问
String url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName) //存储桶
.object(fileName) //文件名
.expiry(15) // 设置过期时间,单位秒
.build());
return url;
}
/**
* 构建永久访问地址
* @param fileName
* @return
* @throws Exception
*/
@GetMapping("/getPublicUrl")
public String getDownloadUrl(@RequestParam("fileName") String fileName) throws Exception {
// 构建永久访问地址,前提是这个存储桶允许公开访问
String url = minioClient.getObjectUrl(bucketName, fileName);
return url;
}
}
2.3.6、编写上传页面
在resources/static
目录下,创建index.html
文件,编写上传页面。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件上传</title>
</head>
<body>
<h1>文件上传</h1>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" required>
<button type="submit">上传</button>
</form>
</body>
</html>
2.3.7、最后验证一下服务
最后,将服务启动,一起来验证一下代码的正确性。
1)上传服务验证
在浏览器端,访问http://127.0.0.1:8080/
,选择文件并上传,示例如下。
回到 minio 控制台,可以看到刚刚上传的文件信息。
2)文件预览地址验证
在浏览器端,访问http://127.0.0.1:8080/getPreviewUrl?fileName=图片.jpeg
,会返回一段带有签名的文件预览地址,示例如下。
将其地址复制出来直接访问,可以清晰的看到图片能正常展示。
通过getPresignedObjectUrl()
方法生成的文件地址链接,无论是是公有桶还是私有桶,都可以正常访问。与getObjectUrl()
方法生成的文件预览地址相比,它带有过期时间,这样设计的目的也是为了保护文件资源,避免频繁窃取。
03、小结
最后总结一下,本文主要围绕利用 minio 实现对象存储服务,进行了一次知识内容的总结,如果有描述不对的地方,欢迎留言指出。
在实际的使用过程中,通常会这样处理。
- 如果当前文件不包含隐私信息,比如图片,可以配置公共访问权限,构建永久访问链接。
- 如果当前文件包含隐私信息,比如营业执照图片,可以配置私有桶,构建带有有效时长的访问链接,比如配置过期时间1小时等。
示例代码地址:
https://gitee.com/pzblogs/spring-boot-example-demo