对象储存服务MinIO
对象存储
数据存储一般可分为三种类型:
- 块存储
- 文件存储
- 对象存储
块存储一般指常见的卷或硬盘存储,以及相应的磁盘阵列、NAS、SAN等存储方式,操作对象是磁盘,使用逻辑块编号寻址,数据按字节方式访问,读写速度快。
文件存储则将数据以不同应用程序要求的结构方式组成不同类型的文件,可对文件进行创建、查找、修改、删除等操作,易于实现数据共享。
对象存储将文件以对象的方式进行存储(一个对象包含属性以及内容),通常实现方式为多台分布式服务器内置大容量硬盘,通过对象存储软件组建集群,对外提供读写访问功能。对象存储结合了块存储读写效率快、存储空间可扩展以及文件存储方便共享的优点。
对象存储和文件存储
比较对象存储和文件存储
对象存储 | 传统文件系统 | |
---|---|---|
接口 | 对象存储提供的接口是REST形式的,支持基于HTTP/HTTPS协议的RESTful Web API,通过HTTP/HTTPS请求中的PUT、POST、GET等操作进行文件的上传和下载,通过DELETE操作删除文件。 | POSIX兼容的文件系统,提供open、close、read、write和lseek等接口。 |
数据组织结构 | 对象存储采用扁平的数据组织结构,只有两层结构:所有的文件/对象(Object)都必须存储在某一个存储空间内(Bucket),而且存储空间不能嵌套。 对象存储的扁平数据组织形式和K/V访问方式更能满足数据管理的需求。 | 树状索引结,数百万、千万甚至上亿个文件/对象,单位时间内的访问次数和并发访问量也达到了前所未有的量级,在这种情况下,目录树会给存储系统带来很大的开销和诸多问题。 |
对象存储和文件存储之间的主要区别在于数据结构和可扩展性。文件存储被组织成具有目录和文件夹的层次结构。文件存储还遵循严格的文件协议,例如 SMB、NFS 或 Lustre。对象存储使用带有元数据的平面结构和每个对象的唯一标识符,使用标识符可以更轻松地在潜在的数十亿个对象中找到特定对象。
由于这些结构上的差异,文件存储和对象存储具有不同的扩展能力。对象存储可提供近乎无限的扩展性,可扩展到 PB 级和数十亿个对象。由于固有的层次结构和路径,文件存储会遇到扩展限制。
数据块存储
块存储器允许创建原始存储卷,而基于服务器的操作系统可以连接到这些卷。 您可以将这些原始卷视为单独的硬盘驱动器。 因此,您可以将块存储器用于几乎任何类型的应用,包括文件存储、数据库存储、虚拟机文件系统 (VMFS) 卷等。
例如,在企业中部署虚拟机。 通过使用块存储器,您可以轻松创建和格式化基于块的存储卷来存储 VMFS。 然后,物理服务器可以连接到该块并创建多个虚拟机。 更重要的是,创建基于块的卷、安装操作系统并连接到该卷将允许用户使用该本机操作系统共享文件。
数据库或 ERP 系统等其他企业应用程序通常需要针对每个主机的专用低延迟存储。这种存储与直接连接存储(DAS)或存储区域网络(SAN)类似。基于数据块的云存储解决方案使用各个虚拟服务器进行预置,可提供高性能工作负载所需的超低延迟。
比较对象存储和数据块存储
对象存储最适合用于大量非结构化数据,尤其是当持久性、无限存储、可扩展性和复杂的元数据管理是影响整体性能的相关因素时。
数据块存储可在各种使用案例中提供低延迟和高性能值。其功能主要用于结构化数据库存储、VM 文件系统卷,以及大量读取和写入负载。
FileOperationService 接口
fastms.file.storage
local LocalFileOperationServiceImpl
minio MinioFileOperationServiceImpl
fastdfs FastdfsFileOperationServiceImpl
对象存储云服务
对象存储云服务:
- AWS S3
- 华为云 OBS
- 阿里云 OSS
- 腾讯云 COS
- 金山云 Ks3
S3 API
S3指的是亚马逊旗下的云对象存储服务 AWS S3,因为AWS S3 是前辈,所以国内几乎所有的云服务厂商都兼容S3 API,包括阿里云、腾讯云、金山云等。
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.12.630</version>
</dependency>
API 兼容
腾讯云 COS
腾讯云 COS 提供了 AWS S3 兼容的 API,因此当您的数据从 S3 迁移到 COS 之后,只需要进行简单的配置修改,即可让您的客户端应用轻松兼容 COS 服务。 https://cloud.tencent.com/document/product/436/37421
阿里云OSS
阿里云OSS提供了S3 API的兼容性,可以将您的数据从Amazon S3无缝迁移至阿里云OSS。OSS兼容S3协议。您可以通过S3 SDK或者支持S3协议的工具执行创建Bucket、上传Object等相关操作。
OSS对S3 Bucket、Object以及Multipart操作兼容的API如下:
操作类型 | API |
---|---|
Bucket操作 | PutBucket DeleteBucket GetBucket GetBucketACL GetBucketLifecycle GetBucketLocation GetBucketLogging HeadBucket PutBucketACL PutBucketLifecycle PutBucketLogging |
Object操作 | DeleteObject DeleteObjects GetObject GetObjectACL HeadObject PostObject PutObject PutObjectCopy PutObjectACL |
Multipart 分块操作 | InitiateMultipartUpload AbortMultipartUpload CompleteMultipartUpload ListParts UploadPart UploadPartCopy |
金山云KS3
目前KS3 API接口覆盖了AWS S3协议大部分的基本功能,并在持续扩充新特性。除少量AWS S3特殊特性接口未覆盖外,用户采用AWS S3协议的软件、服务基本上可以做到无缝迁移。https://docs.ksyun.com/documents/959
文件上传
简单上传 putObjet :文件、流
表单上传 postObjet
分块上传 uploadPart
文件下载
存储类型
OSS支持标准(Standard)、低频访问(IA)和归档存储(Archive)三种存储类型,分别对应Amazon S3中的STANDARD、STANDARD_IA和GLACIER。您可以根据需要转换OSS Object的存储类型。
MinIO
介绍
对象存储的出现是为解决了存储海量大数据的问题,如存储海量的视频、图片,并进行数据归档、数据备份、大数据分析等操作。对象存储一般采用key-object的扁平化存储架构,使用方便,调用API就可进行数据的多样化读写。其大容量、动态扩展、数据灾备等性能,是传统文件存储和NAS无法比拟的。
MinIO是一套基于Apache License V2.0协议的轻量级、高性能开源对象存储框架,适用于图片、视频、镜像等海量非结构化数据存储。MinIO采用Golang实现,客户端支持Java、Python、JavaScript、Golang语言,兼容亚马逊S3云存储服务接口,方便与其他应用结合。
1)存储机制。MinIO使用纠删码(erasure code)和校验和(checksum)来保护数据免受硬件故障和无声数据损坏,可保证N/2节点损坏的情况下数据的正常访问。
2)扩展性。极简性和扩展性是MinIO集群的两个重要设计理念。MinIO支持对等扩容和联邦扩容两种扩容方式。对等扩容,即通过增加对等的集群节点和磁盘以扩充集群,例如,原集群包含4个节点4块磁盘,则扩容时可同样增加4个节点4个磁盘(或其倍数),以此保证系统可维持相同的数据冗余SLA,降低扩展的复杂性。联邦扩容,其基本原理是引入etcd作为统一命名空间,将多个MinIO集群组成一个联邦,可在原有联邦的基础上,向联邦中增加新集群,此扩容方式理论可实现无限扩展,且可以实现在原联邦服务不中断的情况下扩容。
3)对外服务。MinIO完全兼容S3标准接口,客户端和服务端之间通过http/https进行通信。MinIO提供客户端mc(MinIO Client)以支持UNIX命令,同时支持多语言的客户端SDK。此外,其存储后端除使用磁盘外,还可通过网关对接其他存储系统与资源。具体如下表所示。

4)多媒体拉流支持。MinIO对于多媒体文件,支持HTTP-Range的方式在线拉流播放与音视频进度条拖拽。如下图,使用浏览器以流的形式访问存储于MinIO的多媒体文件时,每拖动一次进度条,则会向MinIO服务端发送一条Http-Request请求,请求Headers中包含Range字段,其内容是当前请求视频进度的开始字节数及缓存结束字节数。这种形式使MinIO天生支持多媒体文件的拉流播放与进度拖拽。
安装部署
Windows启动
.\minio.exe server D:\minio\data --console-address :9090
指定文件存储位置和web控制台端口
成功启动后在命令控制台输出:
Console: http://192.0.2.10:9090 http://127.0.0.1:9090
RootUser: minioadmin
RootPass: minioadmin
docker 部署
mkdir -p ~/minio/data
docker run -d \
-p 19002:9000 \
-p 19003:9001 \
--name minio \
-v ~/minio/data:/data \
-e "MINIO_ROOT_USER=minioadmin" \
-e "MINIO_ROOT_PASSWORD=minioadmin" \
--restart=always \
quay.io/minio/minio server /data --console-address ":9001"
上传对象
JS JDK:(必须基于NodeJS)
fPutObject : 单个对象的最大大小限制为 5TB, 如果大于64M自动采用分块上传。上传的数据使用 MD5SUM 签名进行验证。
var file = '/tmp/40mbfile'
var metaData = {
'Content-Type': 'text/html',
}
minioClient.fPutObject('mybucket', '6bb9f982-574a-432d-9cfd-4a30337d5a73.jpg ', file, metaData, function (err, objInfo) {
if (err) {
return console.log(err)
}
console.log('Success', objInfo.etag, objInfo.versionId)
})
注意,目前官方没有提供纯JS的SDK,必须基于NodeJS环境。
如果没有NodeJS环境,想在浏览器直接上传文件到对象存储服务器,可以直接发起HTTP请求。
生成预签名 URL
public String getPresignedObjectUrl(String fileName, String contentType){
String suffix = fileName.substring(fileName.lastIndexOf("."));
String key = UUID.randomUUID().toString() + suffix;
Map<String, String> headers = new HashMap<>();
if(StringUtils.isEmpty(contentType)){
headers.put("response-content-type", "application/ocet-stream");
}else{
headers.put("response-content-type", contentType);
}
try {
return minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder().
bucket(minIOConfig.getBucketName()).
object(key).
method(Method.PUT).
expiry(12, TimeUnit.HOURS).
extraQueryParams(headers).
build());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
返回结果:
http://112.126.64.163:19000/test-education/a5ba30af-f384-4c5b-8547-7253fc685832.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=3L5oOYFk03g6yElJgqKQ%2F20240126%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20240126T033343Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=bcf9c3c34d5db7087be7c7356ff10441b3fecaff8b9252e0d57f12520fb9d2df
public void test() {
String bucketName = "my-bucketname";
String objectName = "my-objectname";
//获取对象的数据。返回的 InputStream 使用后必须关闭才能释放网络资源。
try (GetObjectResponse inputStream = minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());) {
// Read data from stream
//将对象的数据下载到文件。
minioClient.downloadObject(
DownloadObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.filename("my-object-file")
.build());
int size = 1024;
// 上传已知大小的输入流。
ObjectWriteResponse objectWriteResponse = minioClient.putObject(
PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(
inputStream, size, -1)
.contentType("video/mp4")
.build());
String object = objectWriteResponse.object();
// 上传未知大小的输入流。
minioClient.putObject(
PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(
inputStream, -1, 10485760)
.contentType("video/mp4")
.build());
// 上传文件
minioClient.uploadObject(
UploadObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.filename("my-video.avi")
.contentType("video/mp4")
.build());
// 删除一个对象
minioClient.removeObject(
RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
// 删除一个对象的指定版本
minioClient.removeObject(
RemoveObjectArgs.builder()
.bucket(bucketName)
.object("my-versioned-objectname")
.versionId("my-versionid")
.build());
//检查指定bucket是否存在
boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
//创建bucket
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
//查看所有bucket
List<Bucket> bucketList = minioClient.listBuckets();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
COS
生成预签名 URL(更安全)
//生成预签名 URL(更安全)
Date expirationDate = new Date(System.currentTimeMillis() + 60L * 60L * 1000L);// 这里设置签名在1个小时后过期
URL presignedUrl = cosclient.generatePresignedUrl(bucketName, fileName, expirationDate);
返回结果:
http://112.126.64.163:19000/test-education/a5ba30af-f384-4c5b-8547-7253fc685832.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=3L5oOYFk03g6yElJgqKQ%2F20240126%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20240126T033343Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=bcf9c3c34d5db7087be7c7356ff10441b3fecaff8b9252e0d57f12520fb9d2df
生成对象URL
URL objectUrl = cosclient.getObjectUrl(bucketName, fileName);
Ks3
// 生成一个在1000秒后过期的外链
public String getPresignedObjectUrl(String fileName,String contentType) {
String suffix = fileName.substring(fileName.lastIndexOf("."));
String key = UUID.randomUUID() + suffix;
GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest();
req.setMethod(HttpMethod.PUT);
req.setBucket(ks3Config.getBucketName());
req.setKey(key);
// req.setExpiration();// 不指定的话则默认为15分钟后过期
// 设置acl为公开读,不加该header则默认为私有,生成外链时设置了header,则在使用外链的时候也需要添加相应的header
req.getRequestConfig().getExtendHeaders().put("x-kss-acl", "public-read");
// 设置文件的Content-Type,具体值请根据时间情况设定。在使用外链的时候需要把Content-Type设置成指定的值
if(StringUtils.isEmpty(contentType)){
req.setContentType("application/ocet-stream");
}else{
req.setContentType(contentType);
}
// req.setSseAlgorithm("AES256");//设置服务端加密
return ks3Client.generatePresignedUrl(req);
}
返回结果
http://test-education.ks3-cn-beijing.ksyuncs.com/3ae10882-aaca-4b6b-9233-dae970c97ada.jpg?AccessKeyId=AKLTjyRSbYMjTQSLDgCXKmSf&Expires=1706244124&Signature=33tlOzN%2B9%2FRqQRza7D2yJ1JM2I8%3D
迁移与备份
rclone 工具
rclone 是一款的命令行工具,支持在不同对象存储、网盘间同步、上传、下载数据。Rclone多种文件传输协议,支持SFTP,HTTP,WebDAV,FTP 和 DLNA 等,Rclone是一个成熟的开源软件。使用Go 语言开发。
功能
Rclone支持但不仅限于下面列出功能:
- 将文件备份(和加密)到云存储
- 从云存储恢复(和解密)文件
- 将云数据镜像到其他云服务或本地
- 将数据迁移到云端,或在云存储供应商之间迁移
- 将多个、加密、缓存或多样化的云存储挂载为磁盘
- 使用lsf、ljson、size、ncdu分析和说明云存储上保存的数据
- 联合文件系统一起呈现多个本地和/或云文件系统作为一个
rclone 支持超过70种云存储产品:
Amazon S3
Aliyun (OSS)
Tencent Cloud Object Storage (COS)
Minio
The local filesystem
Nextcloud
HDFS
Google Cloud Storage
Google Drive
...
使用 Rclone
可以轻松实现备份同步文件到各大网盘,简化了备份方案,也可以用来迁移2个不同的网盘程序文件,甚至可以直接挂载网盘到本地用来扩充磁盘使用。
README
参考:
Windows平台下使用 Rclone 挂载 OneDrive 为本地硬盘