MinIO框架安装使用+实现上传需求
原文:CSDN(历史文章导入,当前状态为草稿)
什么是MinIO框架
Section titled “什么是MinIO框架”前置知识:对象存储
对象存储服务( Object Storage Service,OSS ):是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。MinIO对象存储系统是为海量数据存储、人工智能、大数据分析而设计,基于Apache License v2.0开源协议的对象存储系统,它完全兼容Amazon S3接口,单个对象最大可达5TB,适合存储海量图片、视频、日志文件、备份数据和容器/虚拟机镜像等。
通常在企业中我们会将一些图片,视频,文档等相关数据存储在对象存储中,常见的对象存储服务有阿里云的OSS对象存储、FastDFS分布式文件系统以及公司的私有云平台等等,以便于数据的存储和快速获取。但随着业务的快速发展,我们需要存储一些身份信息用于审核和实名相关的数据,这部分数据较为敏感,因此对于敏感数据的存储也可以选择Minio来进行自建服务。
如何安装(Docker版)
Section titled “如何安装(Docker版)”MinIO安装非常的简单,在网上你很少会看到有关于MinIo的安装问题,所以基本上你去搜一下就能很容易成功安装,在这里我采用的是Docker去安装它。
但是Docker中会有一些坑在,我基本上凭实力全踩上一遍,所以你跟着我去安装,基本不会出什么问题 。
1. 查询MinIO的服务版本
Section titled “1. 查询MinIO的服务版本”docker search minio2. 拉取MinIO
Section titled “2. 拉取MinIO”docker pull minio/minio下载稳定版本的镜像下载后使用docker images查看下载的镜像操作如下图所示

注意端口要起两个(一个9090,一个9000)!!!查了2个小时!!!
下面的代码不能完全套用,关于用户名,密码,数据卷的绑定记得
docker run -d -p 9000:9000 -p 9090:9090 --name=minio --restart=always -e "MINIO_ROOT_USER=xxx" -e "MINIO_ROOT_PASSWORD=xxx" -v /home/data:/data -v /home/config:/root/.minio minio/minio server /data --console-address ":9000" --address ":9090"报错在docker中没有操作文件的权限
Section titled “报错在docker中没有操作文件的权限”docker: Error response from daemon: Mounts denied:The path /home/data is not shared from the host and is not known to Docker.You can configure shared paths from Docker -> Preferences... -> Resources -> File Sharing.See https://docs.docker.com/desktop/mac for more info.解决:
- 打开Docker客户端的首选项(Preference)
- 找到 Resources下的 FILE SHARING
- 配置你允许让Docker访问使用的目录

然后就大功告成!!!

注意我们访问的地址为:http://127.0.0.1:9090
然后输入用户名和密码就可以登录进去了。
1.找到创建用户界面
Section titled “1.找到创建用户界面”在导航栏中找到identity/user处创建一个用户,点击create user按钮

2. 设置用户信息
Section titled “2. 设置用户信息”用户名密码以及权限写完之后,就可以使用该用户登录MinIO控制台了。

创建用户之后,点击用户弹出用户的基本信息,点击导航栏中的Service Accounts,出现下面的界面

点击右边的Create Access Key,系统会随机生成Create Access Key,点击create按钮后,会展示出它们的信息,这时记得将它们复制粘贴到你的笔记或者存储文件中,后续使用代码上传文件时需要用到这两个key。

3. 创建一个桶
Section titled “3. 创建一个桶”在页面导航栏中找到Administrator/Buckets,点击右上角创建桶按钮

然后输入桶的名字,就大功告成了!

使用MinIO
Section titled “使用MinIO” <!-- minio依赖--> <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.3.3</version> </dependency> <!-- 官方 miniodemo需要的依赖--> <dependency> <groupId>me.tongfei</groupId> <artifactId>progressbar</artifactId> <version>0.7.4</version> </dependency> <!-- 这个依赖最好是在4.8.1以上,就用下面这个一般不出什么问题--> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.9.2</version> </dependency>MinIO的初始化
Section titled “MinIO的初始化”MinioClient minioClient = MinioClient.builder() .endpoint("https://play.min.io") .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG") .build();- builder:用于构建一个Minio客户端的配置对象
- endpoint:指定Minio服务的URL地址和端口号
- credentials:提供认证凭证,这里使用的是用户名(admin)和密码(xxx)
- build:客户端的创建
我们获取到的了MinIOClient对象,就可以进行MinIo的API操作。
存储桶的基本操作
Section titled “存储桶的基本操作”1. 检查桶是否存在
Section titled “1. 检查桶是否存在”- boolean bucketExists(BucketExistsArgs args):检查存储桶是否存在(注意里面的参数还需要创建)
代码如下:
public final static String endPoint = "xxx"; public final static String accessKey = "xxxx"; public final static String secretKey = "xxxx"; public final static String BucketName = "xxxx"; public static void uploadFile() throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { try { // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 MinioClient minoClient = MinioClient.builder() .endpoint(endPoint) .credentials(accessKey,secretKey) .build();
// 存储桶的基本使用,新版需要使用build来构造BucketExistsArgs对象 BucketExistsArgs existsArgs = BucketExistsArgs.builder() .bucket(BucketName) .build(); boolean existBucketFlag = minoClient.bucketExists(existsArgs);
System.out.println("桶是否存在:"+existBucketFlag); } catch (MinioException e) { System.out.println("Error occurred: " + e); } }2. 创建存储桶
Section titled “2. 创建存储桶”- public void makeBucket(MakeBucketArgs args):创建一个启用给定区域和对象锁定功能的存储桶。
代码如下:
public static void uploadFile() throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException { // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 MinioClient minoClient = MinioClient.builder() .endpoint(endPoint) .credentials(accessKey,secretKey) .build(); // 创建存储桶 String bucketName="wang"; //存储桶不存在则创建 if(!minoClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())){ MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(bucketName).build(); minoClient.makeBucket(makeBucketArgs); System.out.println("创建存储桶成功"+bucketName); }else{ System.out.println("已存在"); } }}结果如下:

3. 查询所有桶的列表信息
Section titled “3. 查询所有桶的列表信息”- public List listBuckets():列出所有桶的桶信息
代码如下:
public static void uploadFile() throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException { // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 MinioClient minoClient = MinioClient.builder() .endpoint(endPoint) .credentials(accessKey,secretKey) .build();
List<Bucket> buckets = minoClient.listBuckets(); buckets.forEach(bucket -> { System.out.println("存储桶名称:"+bucket.name()+"存储时间"+bucket.creationDate()); }); }注意:桶的创建时间默认是美国时间,创建桶时我们可以指定桶的时区或者设置 MinIO服务器时区。
结果如下:

4. 删除存储桶
Section titled “4. 删除存储桶”- public void removeBucket(RemoveBucketArgs args):删除一个空桶
注意,如果存储桶不是空桶,删除会报错
代码如下:
public static void uploadFile() throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException { // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 MinioClient minoClient = MinioClient.builder() .endpoint(endPoint) .credentials(accessKey,secretKey) .build(); String bucketName = "wang"; RemoveBucketArgs bucketArgs=RemoveBucketArgs.builder().bucket(bucketName).build(); if(minoClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) { minoClient.removeBucket(bucketArgs); System.out.println("删除存储桶成功!"+bucketName); }else { System.out.println("存储桶不存在,无法删除"); } }对象的基本操作
Section titled “对象的基本操作”PutObject方法
Section titled “PutObject方法”- public ObjectWriteResponse putObject(PutObjectArgs args):将给定的流上传为存储桶的对象
InputStream上传:
public static void uploadFile() throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException { // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 MinioClient minoClient = MinioClient.builder() .endpoint(endPoint) .credentials(accessKey,secretKey) .build(); String bucketName= "testdemo"; //创建InputStream上传 File file = new File("D:\\testUploadFile\\test1.webp"); InputStream inputStream = new FileInputStream(file); long start = System.currentTimeMillis(); //上传流 minoClient.putObject( PutObjectArgs.builder(). bucket(bucketName) .object("one/"+file.getName()) .stream(inputStream,inputStream.available(),-1) .build()); inputStream.close(); long over = System.currentTimeMillis(); System.out.println("uploaded successfully 耗时:"+(over-start)); }结果如下:

需要注意几个点:
- 添加的Object大小不能超过 5GB。
- 默认情况下,如果已存在同名Object且对该Object有访问权限,则新添加的Object将覆盖原有的Object,并返回 200 OK。
- OSS没有文件夹的概念,所有资源都是以文件来存储,但您可以通过创建一个以正斜线(/)结尾,大小为 0的Object来创建模拟文件夹(指定 /后,默认会自动创建)。
- 上传文件是也可以使用SSE-C加密,添加自定义元数据及消息头等操作。
getObject方法
Section titled “getObject方法”- public GetObjectResponse getObject(GetObjectArgs args):获取对象的数据
代码如下:
public static void uploadFile() throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException { // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 MinioClient minoClient = MinioClient.builder() .endpoint(endPoint) .credentials(accessKey, secretKey) .build(); String bucketName = "testdemo"; GetObjectArgs getObjectArgs = GetObjectArgs.builder() .bucket(bucketName) .object("one/test1.webp") .build(); GetObjectResponse objectResponse = minoClient.getObject(getObjectArgs); System.out.println(objectResponse.bucket()); System.out.println(objectResponse); }结果如下:

downloadObject方法
Section titled “downloadObject方法”- public void downloadObject(downloadObjectArgs args):将对象的数据下载到磁盘。
//将对象数据下载到磁盘 DownloadObjectArgs objectArgs = DownloadObjectArgs.builder() .bucket(bucketName) .object("one/test1.webp") .filename("D:\\testUploadFile\\download\\test1.webp") .build(); minoClient.downloadObject(objectArgs);结果

getPresignedObjectUrl方法
Section titled “getPresignedObjectUrl方法”- public String getPresignedObjectUrl(GetPresignedObjectUrlArgs args):获取HTTP方法,到期时间和自定义请求参数的对象的预签名URL。
这个方法的用途在于:
可以提供给不用登录进行图片浏览,第三方共享访问等。
我们还可以对返回 URL,根据业务做一些参数验签等控制。
代码如下:
public static void uploadFile() throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException { // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 MinioClient minoClient = MinioClient.builder() .endpoint(endPoint) .credentials(accessKey, secretKey) .build();
String bucketName = "testdemo"; GetPresignedObjectUrlArgs urlArgs = GetPresignedObjectUrlArgs.builder() .bucket(bucketName) .object("one/test1.webp") .method(Method.GET) .expiry(120, TimeUnit.SECONDS) .build(); String presignedObjectUrl = minoClient.getPresignedObjectUrl(urlArgs); System.out.println(presignedObjectUrl); }结果如下
http://127.0.0.1:9090/testdemo/one/test1.webp?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=wang%2F20230829%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230829T032634Z&X-Amz-Expires=120&X-Amz-SignedHeaders=host&X-Amz-Signature=aa1bbaac4906eeac505cd2602954973ab99189f4674570f078f412b6e7dc7109
返回带签名的URL有点小长,过期之后访问会报错。
getPresignedPostFormData方法
Section titled “getPresignedPostFormData方法”- public Map<String,String> getPresignedPostFormData(PostPolicy policy):获取对的PostPolicy的表单数据以使用POST方法上传其数据。
- 使用此方法,获取对象的上传策略(包含签名、文件信息、路径等),然后使用这些信息采用 POST 方法的表单数据上传数据。也就是可以生成一个临时上传的信息对象,第三方可以使用这些信息,就可以上传文件。
注意:第三方请求中的签名必须和 创建策略中的签名参数等一致,不符合策略要求的就会上传失败。
一般使用场景:
- 第三方请求应用服务器接口,来获取一个上传策略信息
- 第三方使用 Http+访问策略信息直接请求应用服务器接口进行上传文件。
代码如下:
public static void uploadFile() throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException { // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 MinioClient minoClient = MinioClient.builder() .endpoint(endPoint) .credentials(accessKey, secretKey) .build();
String bucketName = "testdemo"; String objectName = "one/test2.webp"; //1.为存储桶创建一个上传策略,过期时间为7天 PostPolicy policy = new PostPolicy(bucketName, ZonedDateTime.now().plusDays(7)); //设置一个参数key-value,值为上传对象的名称(保留为桶中的名字) policy.addEqualsCondition("key",objectName); //添加 Content-Type以”image/“开头,表示只能上传照片 policy.addStartsWithCondition("Content-Type","image/"); //设置上传文件的大小 10 KiB to 10MiB policy.addContentLengthRangeCondition(10*1024,10*1024*1024);
//2. 获取策略的认证令牌,签名等信息 Map<String, String> formData = minoClient.getPresignedPostFormData(policy);
//3. 模拟第三方,使用OkHttp调用Post上传对象 //创建MultipartBody对象 MultipartBody.Builder multipartBuilder = new MultipartBody.Builder(); multipartBuilder.setType(MultipartBody.FORM); for (Map.Entry<String,String> entry : formData.entrySet()){ multipartBuilder.addFormDataPart(entry.getKey(),entry.getValue()); }
multipartBuilder.addFormDataPart("key",objectName); multipartBuilder.addFormDataPart("Content-Type","image/webp"); //todo File uploadFile =new File("D:\\testUploadFile\\test2.webp"); multipartBuilder.addFormDataPart("file",objectName, RequestBody.create(uploadFile,null)); Request request = new Request.Builder() .url(endPoint+bucketName) .post(multipartBuilder.build()) .build(); OkHttpClient httpClient = new OkHttpClient().newBuilder().build(); Response response = httpClient.newCall(request).execute(); if(response.isSuccessful()){ System.out.println("successfully"); }else{ System.out.println("error"); } }结果:

copyObject方法
Section titled “copyObject方法”- public ObjectWriteResponse copyObject(CopyObjectArgs args):通过服务器端从另一个对象复制数据来创建一个对象。
代码如下:
public static void uploadFile() throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException { // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 MinioClient minoClient = MinioClient.builder() .endpoint(endPoint) .credentials(accessKey, secretKey) .build(); String bucketName = "testdemo"; String bucketName2 = "hao"; CopyObjectArgs objectArgs = CopyObjectArgs.builder() .source(CopySource.builder() .bucket(bucketName) .object("one/test2.webp") .build()) .bucket(bucketName2) .object("two/test1.webp") .build(); minoClient.copyObject(objectArgs); }结果如下:

removeObject方法
Section titled “removeObject方法”- public void removeObject(RemoveObjectArgs args):移除一个对象。
代码如下:
public static void uploadFile() throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException { // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 MinioClient minoClient = MinioClient.builder() .endpoint(endPoint) .credentials(accessKey, secretKey) .build(); RemoveObjectArgs objectArgs = RemoveObjectArgs.builder() .bucket(BucketName) .object("one/test2.webp") .build(); minoClient.removeObject(objectArgs); }结果如下:

removeObjects方法
Section titled “removeObjects方法”- public Iterable removeObjects(RemoveObjectsArgs args):懒惰地删除多个对象。它需要迭代返回的 Iterable 以执行删除。
代码如下:
//构建需要删除的对象 List<DeleteObject> objects = new LinkedList<>(); objects.add(new DeleteObject("2023/07/05/1189f7file01.jfif")); objects.add(new DeleteObject("onetest1.webp")); objects.add(new DeleteObject("onetest3.webp")); RemoveObjectsArgs objectsArgs = RemoveObjectsArgs.builder() .bucket(BucketName) .objects(objects) .build(); Iterable<Result<DeleteError>> results = minoClient.removeObjects(objectsArgs);结果如下:

桶对象的信息查询
Section titled “桶对象的信息查询”查询桶下对象
Section titled “查询桶下对象”- public Iterable listObjects(ListObjectsArgs args):列出桶的对象信息。
代码如下:
public static void uploadFile() throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException { // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 MinioClient minoClient = MinioClient.builder() .endpoint(endPoint) .credentials(accessKey, secretKey) .build(); ListObjectsArgs listObjectsArgs = ListObjectsArgs.builder() .bucket(BucketName) .build(); Iterable<Result<Item>> results = minoClient.listObjects(listObjectsArgs); for (Result<Item> result : results) { Item item = result.get(); System.out.println(item.objectName()+"\t"+item.size()); } }结果如下:

递归查询桶下对象
Section titled “递归查询桶下对象”代码如下:
public static void uploadFile() throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException { // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 MinioClient minoClient = MinioClient.builder() .endpoint(endPoint) .credentials(accessKey, secretKey) .build(); ListObjectsArgs listObjectsArgs = ListObjectsArgs.builder() .bucket(BucketName) .recursive(true) .build(); Iterable<Result<Item>> results = minoClient.listObjects(listObjectsArgs);
for (Result<Item> result : results) { Item item = result.get(); System.out.println(item.objectName()+"\t"+item.size()); } }结果如下:

代码如下:
public static void uploadFile() throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException { // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 MinioClient minoClient = MinioClient.builder() .endpoint(endPoint) .credentials(accessKey, secretKey) .build(); ListObjectsArgs listObjectsArgs = ListObjectsArgs.builder() .bucket(BucketName) .startAfter("VipSongsDownload/陶喆 - 爱是个什么东西.mflac") .maxKeys(10) .recursive(true) .build(); Iterable<Result<Item>> results = minoClient.listObjects(listObjectsArgs);
for (Result<Item> result : results) { Item item = result.get(); System.out.println(item.objectName()+"\t"+item.size()); } }
这里列举了一些基本的用法,后面需求方面的慢慢更