使用Minio自建OSS


目录:

  1. 概述
  2. 添加依赖
  3. 配置参数
  4. 配置参数实体
  5. 配置MinioClient
  6. 封装工具类

参考/来源:

概述

官方网址 中文官网

MinIO是Kubernetes原生支持、分布式、高性能对象存储项目。MinIO基于Apache V2 license 100%开放源代码。MinIO的客户能够自动的、无限制、自由免费使用和集成MinIO、自由的创新和创造、自由的去修改、自由的再次发行新的版本和软件。提供丰富的部署文档、健全的SDK,以及多样化应用场景建设方案。

MinIO Java Client SDK提供简单的API来访问任何与Amazon S3兼容的对象存储服务。截至2021年01月13日,Minio官方最新版本8.0.3,Minio中文官网最新版本7.0.2。虽然中文官网延迟一个版本,但是并不影响其在中国用户心目中的地位。针对不熟悉英文阅读技巧的用户,尤其是行业新朋友,具有很高的实用价值。本文仅以7.0.2作为应用版本,整合SpringBoot工程。

最低需求:

  • Java 1.8或更高版本
  • Minio 7.0.2
  • SpringBoot 2.0.2.RELEASE

Docker部署

参考:https://mp.weixin.qq.com/s/kvLZRqgm1lEITm1j6rzJ0A

添加依赖

通过Maven进行依赖管理,请引入如下依赖:

<dependency> 
  <groupId>io.minio</groupId> 
  <artifactId>minio</artifactId> 
  <version>7.0.2</version> 
</dependency>

配置文件

通过ymlMinio对象存储连接参数。

## MinIO对象存储
minio:
  # 地址
  url: https://play.min.io
  # 账号
  access-key: Q3AM3UQ867SPQQA43P2F
  # 密码
  secret-key: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
  # 超时时间(毫秒)
  connect-timeout: 60000
  write-timeout: 60000
  read-timeout: 60000

spring:
  servlet:
    multipart:
      enabled: true #开启文件上传
      max-file-size: 10MB #限制文件上传大小为10M

配置参数实体

通过Spring自动注入方式,实例化properties配置实体对象。

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import lombok.Data;

/**
 * Minio配置
 * @author heayan
 * @version 1.0.0
 * @date 2021年1月7日 下午4:42:37
 */
@Data
@Configuration
@ConfigurationProperties(prefix="minio")
public class MinioProperties {
	
	/**
	 * MinIO 地址
	 */
	private String url;	
	/**
	 * MinIO 账号
	 */
	private String accessKey;	
	/**
	 * MinIO 密码
	 */
	private String secretKey;	
	/**
	 * 连接超时时间(毫秒)
	 */
	private long connectTimeout;	
	/**
	 * 写超时时间(毫秒)
	 */
	private long writeTimeout;	
	/**
	 * 读超时时间(毫秒)
	 */
	private long readTimeout;
}

配置MinioClient

创建Minio对象存储配置类MinioConfig.java。项目启动时自动创建Minio连接对象,并注入Spring容器进行管理。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.minio.MinioClient;
import io.minio.errors.InvalidEndpointException;
import io.minio.errors.InvalidPortException;
import lombok.extern.slf4j.Slf4j;

/**

 * 创建Minio连接并注册至Spring容器
 * @author heayan
 * @version 1.0.0
 * @date 2021年1月7日 下午4:37:19
   */
   @Configuration
   @Slf4j
   public class MinioConfig {

   @Autowired
   private MinioProperties properties;

   @Bean
   MinioClient minioClient(){
   	MinioClient client = null;
   	try {
   		client = new MinioClient(
   			properties.getUrl(),
   			properties.getAccessKey(),
   			properties.getSecretKey()
   		);
   		client.setTimeout(properties.getConnectTimeout(), 
   				properties.getWriteTimeout(), 
   				properties.getReadTimeout());
   	} catch (InvalidEndpointException | InvalidPortException e) {
   		// TODO Auto-generated catch block
   		log.error(e.getMessage(), e);
   	}
   	return client;
   }

}

封装工具类

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.xmlpull.v1.XmlPullParserException;

import io.minio.MinioClient;
import io.minio.Result;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.InsufficientDataException;
import io.minio.errors.InternalException;
import io.minio.errors.InvalidArgumentException;
import io.minio.errors.InvalidBucketNameException;
import io.minio.errors.InvalidEncryptionMetadataException;
import io.minio.errors.NoResponseException;
import io.minio.errors.RegionConflictException;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.Item;
import io.minio.messages.Upload;
import lombok.extern.slf4j.Slf4j;

/**
 * Minio对象存储 工具类
 * 
 * @author heayan
 * @version 1.0.0
 * @date 2021年1月7日 下午3:58:06
 */
@Component
@Slf4j
public class MinioUtils {
	
	/**
	 * 	<h2>存储桶命名规则:</h2>
	 *	1. 值区名称必须介于3到63个字符之间。		
	 *	2. 值区名称只能包含小写字母,数字,点(。)和连字符(-)。		
	 *	3. 值区名称必须以字母或数字开头和结尾。		
	 *	4. 存储桶名称不得格式化为IP地址(例如192.168.5.4)。		
	 *	5. 值区名称不能以开头xn--(对于2020年2月之后创建的值区)。		
	 *	6. 存储桶名称在分区中必须唯一。分区是区域的分组。AWS当前具有三个分区:(aws标准区域),aws-cn(中国区域)和aws-us-gov (AWS GovCloud [美国]区域)。		
	 *	与Amazon S3 Transfer Acceleration一起使用的存储桶的名称中不能带有点(。)。有关传输加速的更多信息,请参阅Amazon S3传输加速。
	 * 
	 */

	/**
	 * 连接对象
	 */
	@Autowired
	private MinioClient minioClient;
	
	/**
	 * 存储桶API:创建一个新的存储桶
	 * @param bucketName 存储桶名称
	 * @author heayan 
	 * @throws XmlPullParserException 
	 * @throws IOException 
	 * @throws InternalException 
	 * @throws ErrorResponseException 
	 * @throws NoResponseException 
	 * @throws InsufficientDataException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidBucketNameException 
	 * @throws InvalidKeyException 
	 * @throws RegionConflictException 
	 * @date 2021年1月7日 下午8:27:49
	 */
	public void makeBucket(String bucketName) throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException, RegionConflictException {
		boolean found = minioClient.bucketExists(bucketName);
		if (found) {
			log.info("[Minio] {} already exists", bucketName);
		} else {
			minioClient.makeBucket(bucketName);
			log.info("[Minio] {} is created successfully", bucketName);
		}
	}
	
	/**
	 * 存储桶API:列出所有存储桶。
	 * @return
	 * @author heayan 
	 * @throws Exception 
	 * @date 2021年1月7日 下午8:28:36
	 */
	public List<String> listBuckets() throws Exception{
		List<String> buckets = null;
		List<Bucket> bucketList = minioClient.listBuckets();
		buckets = bucketList.stream().map(e -> e.name()).collect(Collectors.toList());
		log.info("[Minio] The bucket list is : {}", JacksonUtils.obj2json(buckets));
		return buckets;
	}
	
	/**
	 * 存储桶API:检查存储桶是否存在
	 * @param bucketName 存储桶名称
	 * @return
	 * @author heayan 
	 * @throws XmlPullParserException 
	 * @throws IOException 
	 * @throws InternalException 
	 * @throws ErrorResponseException 
	 * @throws NoResponseException 
	 * @throws InsufficientDataException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidBucketNameException 
	 * @throws InvalidKeyException 
	 * @date 2021年1月7日 下午8:28:55
	 */
	public boolean bucketExists(String bucketName) throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException{
		boolean found = false;
		found = minioClient.bucketExists(bucketName);
		if (found) {
			log.info("[Minio] {} already exists", bucketName);
		} else {
			log.info("[Minio] {} does not exist", bucketName);
		}
		return found;
	}
	
	/**
	 * 
	 * 存储桶API:删除一个存储桶
	 * <p>注意: - removeBucket不会删除存储桶里的对象,你需要通过removeObject API来删除它们。</p>
	 * @param bucketName 存储桶名称
	 * @author heayan 
	 * @throws XmlPullParserException 
	 * @throws IOException 
	 * @throws InternalException 
	 * @throws ErrorResponseException 
	 * @throws NoResponseException 
	 * @throws InsufficientDataException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidBucketNameException 
	 * @throws InvalidKeyException 
	 * @date 2021年1月7日 下午8:29:14
	 */
	public void removeBucket(String bucketName) throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException{
		boolean found = minioClient.bucketExists(bucketName);
		if (found) {
			minioClient.removeBucket(bucketName);
			log.info("[Minio] {} is removed successfully", bucketName);
		} else {
			log.info("[Minio] {} does not exist", bucketName);
		}
	}
	
	/**
	 * 存储桶API:列出某个存储桶中的所有对象
	 * @param  bucketName 存储桶名称
	 * @return
	 * @author heayan 
	 * @throws Exception 
	 * @date 2021年1月7日 下午8:31:32
	 */
	public Iterable<Result<Item>> listObjects(String bucketName) throws Exception{
		Iterable<Result<Item>> myObjects = null;
		// 检查存储桶是否存在。
		boolean found = minioClient.bucketExists(bucketName);
		if (found) {
			// 列出存储桶里的对象
			myObjects = minioClient.listObjects(bucketName);
			List<String> objects = new ArrayList<String>();
			for (Result<Item> result : myObjects) {
				Item item = result.get();
				objects.add(new StringBuffer()
					.append(item.lastModified())
					.append(",")
					.append(item.objectName())
					.toString());
			}
			log.info("[Minio] Objects list is : {}", JacksonUtils.obj2json(objects));
		} else {
			log.info("[Minio] {} does not exist", bucketName);
		}
		return myObjects;
	}
	
	/**
	 * 存储桶API:列出某个存储桶中的所有对象
	 * @param  bucketName 存储桶名称
	 * @param prefix 对象名称的前缀
	 * @param recursive 是否递归查找,如果是false,就模拟文件夹结构查找
	 * @return
	 * @author heayan 
	 * @throws Exception 
	 * @date 2021年1月7日 下午8:31:32
	 */
	public Iterable<Result<Item>> listObjects(String bucketName, 
			String prefix) throws Exception{
		Iterable<Result<Item>> myObjects = null;
		// 检查存储桶是否存在。
		boolean found = minioClient.bucketExists(bucketName);
		if (found) {
			// 列出存储桶里的对象
			myObjects = minioClient.listObjects(bucketName, prefix);
			List<String> objects = new ArrayList<String>();
			for (Result<Item> result : myObjects) {
				Item item = result.get();
				objects.add(new StringBuffer()
					.append(item.lastModified())
					.append(",")
					.append(item.objectName())
					.toString());
			}
			log.info("[Minio] Objects list is : {}", JacksonUtils.obj2json(objects));
		} else {
			log.info("[Minio] {} does not exist", bucketName);
		}
		return myObjects;
	}
	
	/**
	 * 存储桶API:列出存储桶中被部分上传的对象
	 * @param bucketName 存储桶名称
	 * @return
	 * @author heayan 
	 * @throws Exception 
	 * @date 2021年1月7日 下午8:31:55
	 */
	public Iterable<Result<Upload>> listIncompleteUploads(String bucketName) throws Exception{
		Iterable<Result<Upload>> myObjects = null;
		// 检查存储桶是否存在。
		boolean found = minioClient.bucketExists(bucketName);
		if (found) {
			// 列出存储桶中所有未完成的multipart上传的的对象。
			myObjects = minioClient.listIncompleteUploads(bucketName);
			List<String> objects = new ArrayList<String>();
			for (Result<Upload> result : myObjects) {
				Upload upload = result.get();
				objects.add(new StringBuffer()
					.append(upload.uploadId())
					.append(", ")
					.append(upload.objectName())
					.toString());
			}
			log.info("[Minio] Objects list is : {}", JacksonUtils.obj2json(objects));
		} else {
			log.info("[Minio] {} does not exist", bucketName);
		}
		return myObjects;
	}

	/**
	 * 文件对象API: 以流的形式下载一个对象
	 * @param bucketName 存储桶名称
	 * @param objectName 存储桶里的对象名称
	 * @return InputStream 
	 * @author heayan 
	 * @throws Exception 
	 * @date 2021年1月7日 下午8:33:53
	 */
	public InputStream getObject(String bucketName, String objectName) throws Exception{
		InputStream stream = null;
		// 调用statObject()来判断对象是否存在。
		// 如果不存在, statObject()抛出异常,
		// 否则则代表对象存在。
		minioClient.statObject(bucketName, objectName);

		// 获取objectName的输入流。
		stream = minioClient.getObject(bucketName, objectName);
		log.info("[Minio] Get object is : {}", stream);
		return stream;
	}
	
	/**
	 * 文件对象API: 下载并将文件保存到本地
	 * @param bucketName 存储桶名称
	 * @param objectName 存储桶里的对象名称
	 * @param fileName 目标文件名称 示例:photo.jpg
	 * @author heayan 
	 * @throws XmlPullParserException 
	 * @throws IOException 
	 * @throws InternalException 
	 * @throws ErrorResponseException 
	 * @throws NoResponseException 
	 * @throws InsufficientDataException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidBucketNameException 
	 * @throws InvalidKeyException 
	 * @throws InvalidArgumentException 
	 * @date 2021年1月7日 下午8:33:46
	 */
	public void getObject(String bucketName, String objectName, String fileName) throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException, InvalidArgumentException{
		// 调用statObject()来判断对象是否存在。
		// 如果不存在, statObject()抛出异常,
		// 否则则代表对象存在。
		minioClient.statObject(bucketName, objectName);
	
		// 获取objectName的流并保存到fileName文件中。
		minioClient.getObject(bucketName, objectName, fileName);
		log.info("[Minio] Get object {} finished!", objectName);
	}
	
	/**
	 * 文件对象API: 以流的形式下载一个AES加密对象,并进行解密
	 * <p>在给定的存储桶中获取整个加密对象的数据作为InputStream,然后用传入的master key解密和加密对象关联的content key。
	 * 然后创建一个含有InputStream和Cipher的CipherInputStream。这个Cipher被初始为用于使用content key进行解密,
	 * 所以CipherInputStream会在返回数据前,尝试读取数据并进行解密。所以read()方法返回的是处理过的原始对象数据。</p>
	 * <p>CipherInputStream必须用完关闭,否则连接不会被释放。</p>
	 * 
	 * @param bucketName 存储桶名称
	 * @param objectName 存储桶里的对象名称
	 * @param secKey SecretKey类型的数据
	 * @author heayan 
	 * @throws XmlPullParserException 
	 * @throws IOException 
	 * @throws InternalException 
	 * @throws ErrorResponseException 
	 * @throws NoResponseException 
	 * @throws InsufficientDataException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidBucketNameException 
	 * @throws InvalidKeyException 
	 * @throws InvalidEncryptionMetadataException 
	 * @throws InvalidAlgorithmParameterException 
	 * @throws BadPaddingException 
	 * @throws IllegalBlockSizeException 
	 * @throws NoSuchPaddingException 
	 * @throws InvalidArgumentException 
	 * @date 2021年1月7日 下午8:33:39
	 */
	public InputStream getObject(String bucketName, String objectName, SecretKey secKey) throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException, InvalidArgumentException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidEncryptionMetadataException{
		InputStream stream = null;
		// 调用statObject()来判断对象是否存在。
		// 如果不存在, statObject()抛出异常,
		// 否则则代表对象存在。
		minioClient.statObject(bucketName, objectName);

		// 获取对象数据并保存到photo.jpg
		stream = minioClient.getObject(bucketName, objectName, secKey);
		log.info("[Minio] Successfully get object {}/{}", bucketName, objectName);
		return stream;
	}
	
	/**
	 * 文件对象API: 通过InputStream上传对象
	 * <p>前端上传文件时,推荐此方法。</p>
	 * <p>
	 *   单个对象的最大大小限制在5TB。putObject在对象大于5MiB时,自动使用multiple parts方式上传。这样,当上传失败时,
	 *   客户端只需要上传未成功的部分即可(类似断点上传)。上传的对象使用MD5SUM签名进行完整性验证。
	 * </p>
	 * @param bucketName 存储桶名称
	 * @param objectName 存储桶里的对象名称
	 * @param stream 要上传的流
	 * @param contentType Content type
	 * @author heayan 
	 * @throws XmlPullParserException 
	 * @throws IOException 
	 * @throws InternalException 
	 * @throws ErrorResponseException 
	 * @throws NoResponseException 
	 * @throws InsufficientDataException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidBucketNameException 
	 * @throws InvalidKeyException 
	 * @throws RegionConflictException 
	 * @throws InvalidArgumentException 
	 * @date 2021年1月7日 下午8:33:28
	 */
	public void putObject(String bucketName, 
			String objectName, 
			InputStream stream, 
			String contentType) throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException, RegionConflictException, InvalidArgumentException{
		//如果存储桶不存在,则进行创建,创建后文件上传
		boolean found = minioClient.bucketExists(bucketName);
		if(!found){
			minioClient.makeBucket(bucketName);
		}
		minioClient.putObject(bucketName, objectName, stream, contentType);
		log.info("[Minio] Put object {}/{} success, please wait a moment!", bucketName, objectName);
	}
	
	/**
	 * 文件对象API: 通过文件上传到对象中
	 * <p>本地上传文件时,推荐此方法。</p>
	 * @param bucketName
	 * @param objectName 示例:island.jpg
	 * @param fileName 示例:/mnt/photos/island.jpg
	 * @author heayan 
	 * @throws XmlPullParserException 
	 * @throws IOException 
	 * @throws InternalException 
	 * @throws ErrorResponseException 
	 * @throws NoResponseException 
	 * @throws InsufficientDataException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidBucketNameException 
	 * @throws InvalidKeyException 
	 * @throws RegionConflictException 
	 * @throws InvalidArgumentException 
	 * @date 2021年1月7日 下午8:33:20
	 */
	public void putObject(String bucketName, String objectName, String fileName) throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException, RegionConflictException, InvalidArgumentException{
		//如果存储桶不存在,则进行创建,创建后文件上传
		boolean found = minioClient.bucketExists(bucketName);
		if(!found){
			minioClient.makeBucket(bucketName);
		}
		minioClient.putObject(bucketName, objectName, fileName);
		log.info("[Minio] Put object {}/{} success, please wait a moment!", bucketName, objectName);
	}
	
	/**
	 * 文件对象API: 通过文件流上传AES加密对象
	 * 
	 * <p>
	 * 	   拿到流的数据,使用随机生成的content key进行加密,并上传到指定存储桶中。同时将加密后的content key和iv做为加密对象有header也上传到存储桶中。
	 *   content key使用传入到该方法的master key进行加密。如果对象大于5MB,客户端会自动进行multi part上传。
	 * </p>
	 * 
	 * @param bucketName 存储桶名称
	 * @param objectName 存储桶里的对象名称
	 * @param stream 要上传的流
	 * @param size 要上传的流的大小
	 * @param contentType Content type
	 * @param key 用AES初使化的对象SecretKey
	 * @author heayan 
	 * @throws XmlPullParserException 
	 * @throws IOException 
	 * @throws InternalException 
	 * @throws ErrorResponseException 
	 * @throws NoResponseException 
	 * @throws InsufficientDataException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidBucketNameException 
	 * @throws InvalidKeyException 
	 * @throws RegionConflictException 
	 * @throws InvalidAlgorithmParameterException 
	 * @throws BadPaddingException 
	 * @throws IllegalBlockSizeException 
	 * @throws NoSuchPaddingException 
	 * @throws InvalidArgumentException 
	 * @date 2021年1月7日 下午8:33:00
	 */
	public void putObject(String bucketName, 
			String objectName, 
			InputStream stream, 
			long size, 
			String contentType,
			SecretKey secKey) throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException, RegionConflictException, InvalidArgumentException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException{
		//如果存储桶不存在,则进行创建,创建后文件上传
		boolean found = minioClient.bucketExists(bucketName);
		if(!found){
			minioClient.makeBucket(bucketName);
		}
		minioClient.putObject(bucketName, objectName, stream, size, contentType, secKey);
		log.info("[Minio] Put object {}/{} success, please wait a moment!", bucketName, objectName);
	}
	
	/**
	 * 删除一个对象
	 * @param bucketName 存储桶名称
	 * @param objectName 存储桶里的对象名称
	 * @author heayan 
	 * @throws XmlPullParserException 
	 * @throws IOException 
	 * @throws InternalException 
	 * @throws ErrorResponseException 
	 * @throws NoResponseException 
	 * @throws InsufficientDataException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidBucketNameException 
	 * @throws InvalidKeyException 
	 * @date 2021年1月7日 下午8:32:51
	 */
	public void removeObject(String bucketName, String objectName) throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException{
		// 从bucketName中删除objectName。
		minioClient.removeObject(bucketName, objectName);
		log.info("[Minio] Successfully removed {}/{}", bucketName, objectName);
	}
	
	/**
	 * 删除多个对象
	 * @param bucketName 存储桶名称
	 * @param objectNames 含有要删除的多个object名称的迭代器对象
	 * @return Iterable 含有多个删除错误结果的迭代器对象
	 * @author heayan 
	 * @throws XmlPullParserException 
	 * @throws IOException 
	 * @throws InternalException 
	 * @throws ErrorResponseException 
	 * @throws NoResponseException 
	 * @throws InsufficientDataException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidBucketNameException 
	 * @throws InvalidKeyException 
	 * @date 2021年1月7日 下午8:32:40
	 */
	public Iterable<Result<DeleteError>> removeObject(String bucketName, Iterable<String> objectNames) throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException{
		Iterable<Result<DeleteError>> errorResults = null;
		// 删除存储桶里的多个对象
		errorResults = minioClient.removeObject(bucketName, objectNames);
		log.info("[Minio] Successfully removed {}/{}", bucketName, objectNames);
		for (Result<DeleteError> errorResult : errorResults) {
			DeleteError error = errorResult.get();
			log.info("[Minio] Failed to remove '" + error.objectName() + "'. Error:" + error.message());
		}
		return errorResults;
	}
	
	/**
	 * 删除一个未完整上传的对象
	 * @param bucketName 存储桶名称
	 * @param objectName 存储桶里的对象名称
	 * @author heayan 
	 * @throws XmlPullParserException 
	 * @throws IOException 
	 * @throws InternalException 
	 * @throws ErrorResponseException 
	 * @throws NoResponseException 
	 * @throws InsufficientDataException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidBucketNameException 
	 * @throws InvalidKeyException 
	 * @date 2021年1月7日 下午8:32:31
	 */
	public void removeIncompleteUpload(String bucketName, String objectName) throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException{
		// 从存储桶中删除名为objectName的未完整上传的对象。
		minioClient.removeIncompleteUpload(bucketName, objectName);
		log.info("[Minio] Successfully removed all incomplete upload session of {}/{}", bucketName, objectName);
	}
	
	/**
	 * 通过文件流方式上传文件
	 * 
	 * @param bucket
	 * @param objectName
	 * @param stream
	 * @param contentType
	 * @author heayan 
	 * @throws XmlPullParserException 
	 * @throws IOException 
	 * @throws InvalidArgumentException 
	 * @throws RegionConflictException 
	 * @throws InternalException 
	 * @throws ErrorResponseException 
	 * @throws NoResponseException 
	 * @throws InsufficientDataException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidBucketNameException 
	 * @throws InvalidKeyException 
	 * @date 2021年1月11日 下午6:08:32
	 */
	public void upload(String bucket, String objectName, InputStream stream, String contentType) throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, NoResponseException, ErrorResponseException, InternalException, RegionConflictException, InvalidArgumentException, IOException, XmlPullParserException{
		putObject(bucket, objectName, stream, contentType);
	}
	
	/**
	 * 通过文件流方式下载
	 * <p>特别说明:重载该方法时,必须关闭文件流。</p>
	 * @param path
	 * @param filename
	 * @param HttpServletResponse 
	 * @throws Exception 
	 */
	public void download(HttpServletResponse response,
			String bucket, 
			String objectName, 
			String fileName) throws Exception{
		//获取文件流
		InputStream is = getObject(bucket, objectName);
		getObject(bucket, objectName, "C:\\Users\\wushu\\Desktop\\111.jpg");
		
		//设置响应信息
		response.reset();
		response.setCharacterEncoding("utf-8");
		response.setContentType("application/octet-stream");
		response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
		OutputStream os = response.getOutputStream();
      
		// 返回文件流
		try {
			log.info("-->chagndu :" + is.available());
			byte[] b = new byte[1024];
			int len = 0;
			while ((len = is.read(b)) != -1) {
				os.write(b, 0, len);
			}
			os.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			log.error(e.getMessage(), e);
			throw e;
		} finally {
			try {
				if (os != null) {
					os.close();
				}
				if (is != null) {
					is.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				log.error(e.getMessage(), e);
			}
		}
	}
}

控制类示例

参考:http://www.macrozheng.com/#/technology/minio_use

/**
 * Created by macro on 2019/12/25.
 */
@Api(tags = "MinioController", description = "MinIO对象存储管理")
@Controller
@RequestMapping("/minio")
public class MinioController {

    private static final Logger LOGGER = LoggerFactory.getLogger(MinioController.class);
    @Value("${minio.endpoint}")
    private String ENDPOINT;
    @Value("${minio.bucketName}")
    private String BUCKET_NAME;
    @Value("${minio.accessKey}")
    private String ACCESS_KEY;
    @Value("${minio.secretKey}")
    private String SECRET_KEY;

    @ApiOperation("文件上传")
    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult upload(@RequestParam("file") MultipartFile file) {
        try {
            //创建一个MinIO的Java客户端
            MinioClient minioClient = new MinioClient(ENDPOINT, ACCESS_KEY, SECRET_KEY);
            boolean isExist = minioClient.bucketExists(BUCKET_NAME);
            if (isExist) {
                LOGGER.info("存储桶已经存在!");
            } else {
                //创建存储桶并设置只读权限
                minioClient.makeBucket(BUCKET_NAME);
                minioClient.setBucketPolicy(BUCKET_NAME, "*.*", PolicyType.READ_ONLY);
            }
            String filename = file.getOriginalFilename();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            // 设置存储对象名称
            String objectName = sdf.format(new Date()) + "/" + filename;
            // 使用putObject上传一个文件到存储桶中
            minioClient.putObject(BUCKET_NAME, objectName, file.getInputStream(), file.getContentType());
            LOGGER.info("文件上传成功!");
            MinioUploadDto minioUploadDto = new MinioUploadDto();
            minioUploadDto.setName(filename);
            minioUploadDto.setUrl(ENDPOINT + "/" + BUCKET_NAME + "/" + objectName);
            return CommonResult.success(minioUploadDto);
        } catch (Exception e) {
            LOGGER.info("上传发生错误: {}!", e.getMessage());
        }
        return CommonResult.failed();
    }

    @ApiOperation("文件删除")
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult delete(@RequestParam("objectName") String objectName) {
        try {
            MinioClient minioClient = new MinioClient(ENDPOINT, ACCESS_KEY, SECRET_KEY);
            minioClient.removeObject(BUCKET_NAME, objectName);
            return CommonResult.success(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return CommonResult.failed();
    }
}

文章作者: 小小千千
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小小千千 !
评论
  目录