介绍
MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
安装
Windows安装包安装
1、下载exe文件下载链接
2、cmd运行脚本: .\minio.exe server D:\html\minio(为minion运行目录,文件上传到这里)
linux环境docker安装
1、拉取docker镜像
docker search minio
docker pull minio/minio
2、启动容器
docker run -itd -p 9000:9000
--name minio
-e "MINIO_ACCESS_KEY=admin"
-e "MINIO_SECRET_KEY=admin"
-v /mnt/data:/data
-v /mnt/config:/root/.minio
minio/minio server /data
安装完成,访问9000端口即可打开web页面进行操作
整合springboot
1、依赖
<!--minio对象存储-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.1.4</version>
</dependency>
2、配置yml minio参数
#minio
minio:
endpoint: 127.0.0.1
port: 9000
accessKey: admin
secretKey: admin
secure: false
3、配置minio客户端
属性类
@Data
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
private String endpoint;
private int port;
private String accessKey;
private String secretKey;
private Boolean secure;
}
配置类
@Configuration
@EnableConfigurationProperties({MinioProperties.class})
public class MinioConfiguration {
@Autowired
private MinioProperties properties;
@Bean
MinioClient minioClient() {
return MinioClient.builder()
.endpoint(properties.getEndpoint(),properties.getPort(),properties.getSecure())
.credentials(properties.getAccessKey(),properties.getSecretKey())
.build();
}
}
工具类
@Slf4j
@Component
public class MinioHelper {
@Autowired
private MinioClient minioClient;
private static final int DEFAULT_EXPIRY_TIME = 7 * 24 * 3600;
/**检查存储桶是否存在*/
@SneakyThrows
public boolean bucketExists(String bucketName) {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
}
/**创建存储桶*/
@SneakyThrows
public boolean makeBucket(String bucketName) {
boolean flag = bucketExists(bucketName);
if (!flag) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
return true;
}
return false;
}
/**列出所有存储桶名称*/
public List<String> listBucketNames() {
List<Bucket> bucketList = listBuckets();
List<String> bucketListName = new ArrayList<>();
for (Bucket bucket : bucketList) {
bucketListName.add(bucket.name());
}
return bucketListName;
}
/**列出所有存储桶*/
@SneakyThrows
public List<Bucket> listBuckets() {
return minioClient.listBuckets();
}
/**删除存储桶*/
@SneakyThrows
public void removeBucket(String bucketName) {
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
Iterable<Result<Item>> myObjects = listObjects(bucketName);
for (Result<Item> result : myObjects) {
Item item = result.get();
AssertUtils.assertTrue(item.size() > 0,SystemCode.BIZ_EXCEPTION,"bucket有对象文件,删除失败");
}
// 删除存储桶,注意,只有存储桶为空时才能删除成功。
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
flag = bucketExists(bucketName);
AssertUtils.assertFalse(flag,SystemCode.BIZ_EXCEPTION,"bucket未成功删除");
}
/**列出存储桶中的所有对象名称*/
@SneakyThrows
public List<String> listObjectNames(String bucketName) {
List<String> listObjectNames = new ArrayList<>();
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
Iterable<Result<Item>> myObjects = listObjects(bucketName);
for (Result<Item> result : myObjects) {
Item item = result.get();
listObjectNames.add(item.objectName());
}
return listObjectNames;
}
/**列出存储桶中的所有对象*/
@SneakyThrows
public Iterable<Result<Item>> listObjects(String bucketName) {
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
return minioClient.listObjects(ListObjectsArgs.builder()
.bucket(bucketName)
.recursive(false)
.includeUserMetadata(false)
.useApiVersion1(false)
.build());
}
/**通过文件上传到对象*/
@SneakyThrows
public boolean putObject(String bucketName, String objectName, String fileName) {
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
minioClient.uploadObject(UploadObjectArgs.builder().bucket(bucketName).object(objectName).filename(fileName).build());
ObjectStat statObject = statObject(bucketName, objectName);
return statObject != null && statObject.length() > 0;
}
/**文件上传*/
@SneakyThrows
public void putObject(String bucketName, MultipartFile multipartFile, String filename) {
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
PutObjectOptions options = new PutObjectOptions(multipartFile.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE);
options.setContentType(multipartFile.getContentType());
InputStream stream = multipartFile.getInputStream();
minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(filename).stream(
stream, options.objectSize(), options.partSize())
.contentType(options.contentType())
.headers(options.headers())
.sse(options.sse())
.build());
}
/**通过InputStream上传对象*/
@SneakyThrows
public boolean putObject(String bucketName, String objectName, InputStream stream) {
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
PutObjectOptions options = new PutObjectOptions(stream.available(), -1);
minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(
stream, options.objectSize(), options.partSize())
.contentType(options.contentType())
.headers(options.headers())
.sse(options.sse())
.build());
ObjectStat statObject = statObject(bucketName, objectName);
return statObject != null && statObject.length() > 0;
}
/**以流的形式获取一个文件对象*/
@SneakyThrows
public InputStream getObject(String bucketName, String objectName) {
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
ObjectStat statObject = statObject(bucketName, objectName);
if (statObject != null && statObject.length() > 0) {
InputStream stream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
return stream;
}
return null;
}
/**以流的形式获取一个文件对象(断点下载)*/
@SneakyThrows
public InputStream getObject(String bucketName, String objectName, long offset, Long length) {
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
ObjectStat statObject = statObject(bucketName, objectName);
if (statObject != null && statObject.length() > 0) {
InputStream stream = minioClient.getObject(GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.offset(offset)
.length(length)
.build());
return stream;
}
return null;
}
/**下载并将文件保存到本地*/
@SneakyThrows
public boolean getObject(String bucketName, String objectName, String fileName) {
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
ObjectStat statObject = statObject(bucketName, objectName);
if (statObject != null && statObject.length() > 0) {
minioClient.downloadObject(DownloadObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.filename(fileName)
.build());
return true;
}
return false;
}
/**删除一个对象*/
@SneakyThrows
public void removeObject(String bucketName, String objectName) {
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
}
/**删除指定桶的多个文件对象,返回删除错误的对象列表,全部删除成功,返回空列表*/
@SneakyThrows
public List<String> removeObject(String bucketName, List<String> objectNames) {
List<String> deleteErrorNames = new ArrayList<>();
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
Stream<DeleteObject> stream = StreamSupport.stream(objectNames.spliterator(), false)
.map(name -> new DeleteObject(name));
Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(stream::iterator).build());
for (Result<DeleteError> result : results) {
DeleteError error = result.get();
deleteErrorNames.add(error.objectName());
}
return deleteErrorNames;
}
/**生成一个给HTTP GET请求用的presigned URL。浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。*/
@SneakyThrows
public String presignedGetObject(String bucketName, String objectName, Integer expires) {
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
throw new InvalidExpiresRangeException(expires,
"expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME);
}
return minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(objectName)
.expiry(expires)
.build());
}
/**生成一个给HTTP PUT请求用的presigned URL。
* 浏览器/移动端的客户端可以用这个URL进行上传,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天*/
@SneakyThrows
public String presignedPutObject(String bucketName, String objectName, Integer expires) {
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
throw new InvalidExpiresRangeException(expires,
"expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME);
}
return minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.PUT)
.bucket(bucketName)
.object(objectName)
.expiry(expires)
.build());
}
/**获取对象的元数据*/
@SneakyThrows
public ObjectStat statObject(String bucketName, String objectName) {
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
ObjectStat statObject = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
return statObject;
}
/**文件访问路径*/
@SneakyThrows
public String getObjectUrl(String bucketName, String objectName) {
boolean flag = bucketExists(bucketName);
AssertUtils.assertTrue(flag,SystemCode.BIZ_EXCEPTION,"bucket不存在");
return minioClient.getObjectUrl(bucketName, objectName);
}
public void downloadFile(String bucketName, String fileName, String originalName, HttpServletResponse response) {
try {
InputStream file = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(originalName).build());
String filename = new String(fileName.getBytes("ISO8859-1"), StandardCharsets.UTF_8);
if (StringUtils.isNotEmpty(originalName)) {
filename = originalName;
}
response.setHeader("Content-Disposition", "attachment;filename=" + filename);
ServletOutputStream servletOutputStream = response.getOutputStream();
int len;
byte[] buffer = new byte[1024];
while ((len = file.read(buffer)) > 0) {
servletOutputStream.write(buffer, 0, len);
}
servletOutputStream.flush();
file.close();
servletOutputStream.close();
} catch (ErrorResponseException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
完成手工