springboot集成Minio
1、Minio单击安装
mkdir /data/minio cd /data/minio chmod +x minio mkdir data mkdir logs wget https://dl.min.io/server/minio/release/linux-amd64/minio
2、将Minio的账密写入环境变量中
vim /etc/profile export MINIO_ROOT_USER=username export MINIO_ROOT_PASSWORD=password source /etc/profile
3、启动Minio(9002-server端口,9001-web端口)
nohup ./minio server --address :9002 --console-address :9001 /data/minio/data > /data/minio/logs/minio.log 2>&1 &
4、Springboot依赖
<dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>7.1.0</version> </dependency>
5、yml配置
server: port: 8084 servlet: context-path: /minio minio: endpoint: http://ip:9002 domain: ${minio.endpoint}/${minio.bucketName} bucketName: zjk-minio accessKey: root secretKey: password secure: false spring: servlet: multipart: max-file-size: 100MB max-request-size: 150MB
6、MinioConfig
package com.zjk.config; import io.minio.MinioClient; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Data @Configuration @ConfigurationProperties(prefix = "minio") public class MinioConfig { private String endpoint; private String domain; private int port; private String accessKey; private String secretKey; private String bucketName; @Bean public MinioClient getMinioClient(){ MinioClient minioClient = MinioClient.builder().endpoint(endpoint) .credentials(accessKey, secretKey) .build(); return minioClient; } }
7、实现类
package com.zjk.service.impl; import com.zjk.config.MinioConfig; import com.zjk.service.ISysFileService; import io.minio.*; import io.minio.errors.*; import io.minio.http.Method; import io.minio.messages.Bucket; import io.minio.messages.DeleteError; import io.minio.messages.DeleteObject; import io.minio.messages.Item; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; /** * Minio 文件存储 * * @author analyse-cities */ @Service @Slf4j public class MinioSysFileServiceImpl implements ISysFileService { /** * 默认大小 500M */ public static final long DEFAULT_MAX_SIZE = 512 * 1024 * 1024; private static final int DEFAULT_EXPIRY_TIME = 7 * 24 * 3600; /** * 默认的文件名最大长度 100 */ public static final int DEFAULT_FILE_NAME_LENGTH = 100; @Autowired private MinioConfig minioConfig; @Autowired private MinioClient minioClient; /** * 本地文件上传接口 * * @param bucketName * @param file 上传的文件 * @return 访问地址 */ @Override public String uploadFile(String bucketName, MultipartFile file) throws Exception { String fileName = file.getOriginalFilename(); PutObjectArgs args = PutObjectArgs.builder() .bucket(bucketName) .object(fileName) .stream(file.getInputStream(), file.getSize(), -1) .contentType(file.getContentType()) .build(); minioClient.putObject(args); return minioConfig.getDomain() + "/" + fileName; } @Override public List<String> modulesMultiUpload(String bucketName, MultipartFile[] files) throws Exception { List<String> urlList = new ArrayList<>(files.length); for (MultipartFile file : files) { String url = uploadFile(bucketName, file); urlList.add(url); } return urlList; } @Override public void download(String bucketName, String fileName, HttpServletResponse response) { InputStream inputStream = null; OutputStream outputStream = null; try { outputStream = response.getOutputStream(); // 获取文件对象 inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName.replaceFirst("/" + bucketName + "/", "")).build()); byte[] buf = new byte[1024]; int length = 0; response.reset(); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName.substring(fileName.lastIndexOf("/") + 1), "UTF-8")); response.setContentType("application/octet-stream"); response.setCharacterEncoding("UTF-8"); // 输出文件 while ((length = inputStream.read(buf)) > 0) { outputStream.write(buf, 0, length); } inputStream.close(); } catch (Throwable ex) { response.setHeader("Content-type", "text/html;charset=UTF-8"); String data = "文件下载失败"; try { OutputStream ps = response.getOutputStream(); ps.write(data.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { log.error("minio文件下载失败", e); } } finally { try { if (outputStream != null) { outputStream.close(); } if (inputStream != null) { inputStream.close(); } } catch (IOException e) { log.error("minio文件流关闭失败", e); } } } /** * 检查存储桶是否存在 * * @param bucketName 存储桶名称 * @return boolean */ public boolean bucketExists(String bucketName) throws ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); } /** * 创建存储桶 * * @param bucketName 存储桶名称 */ public boolean makeBucket(String bucketName) throws ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, RegionConflictException { boolean flag = bucketExists(bucketName); if (!flag) { minioClient.makeBucket( MakeBucketArgs.builder() .bucket(bucketName) .build()); return true; } else { return false; } } /** * 列出所有存储桶名称 * * @return List<String> */ public List<String> listBucketNames() throws ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { List<Bucket> bucketList = minioClient.listBuckets(); List<String> bucketListName = new ArrayList<>(); for (Bucket bucket : bucketList) { bucketListName.add(bucket.name()); } return bucketListName; } /** * 删除存储桶 * * @param bucketName 存储桶名称 * @return boolean */ public boolean removeBucket(String bucketName) throws ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { boolean flag = bucketExists(bucketName); if (flag) { Iterable<Result<Item>> myObjects = listObjects(bucketName); for (Result<Item> result : myObjects) { Item item = result.get(); // 有对象文件,则删除失败 if (item.size() > 0) { return false; } } // 删除存储桶,注意,只有存储桶为空时才能删除成功。 minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); flag = bucketExists(bucketName); if (!flag) { return true; } } return false; } /** * 列出存储桶中的所有对象 * * @param bucketName 存储桶名称 * @return Iterable<Result<Item>> */ public Iterable<Result<Item>> listObjects(String bucketName) throws XmlParserException, IOException, InvalidResponseException, InvalidKeyException, NoSuchAlgorithmException, ServerException, ErrorResponseException, InvalidBucketNameException, InsufficientDataException, InternalException { boolean flag = bucketExists(bucketName); if (flag) { return minioClient.listObjects( ListObjectsArgs.builder().bucket(bucketName).build()); } return null; } /** * 删除一个对象 * * @param bucketName 存储桶名称 * @param objectName 存储桶里的对象名称 */ public boolean removeObject(String bucketName, String objectName) throws ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { boolean flag = bucketExists(bucketName); if (flag) { minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); return true; } return false; } /** * 删除指定桶的多个文件对象,返回删除错误的对象列表,全部删除成功,返回空列表 * * @param bucketName 存储桶名称 * @param objectNames 含有要删除的多个object名称的迭代器对象 * @return * eg: * List<DeleteObject> objects = new LinkedList<>(); * objects.add(new DeleteObject("my-objectname1")); * objects.add(new DeleteObject("my-objectname2")); * objects.add(new DeleteObject("my-objectname3")); */ public List<String> removeObjects(String bucketName, List<DeleteObject> objectNames) throws ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { List<String> deleteErrorNames = new ArrayList<>(); boolean flag = bucketExists(bucketName); if (flag) { Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(objectNames).build()); for (Result<DeleteError> result : results) { DeleteError error = result.get(); deleteErrorNames.add(error.objectName()); } } return deleteErrorNames; } /** * 生成一个给HTTP GET请求用的presigned URL。 * 浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。 * * @param bucketName 存储桶名称 * @param objectName 存储桶里的对象名称 * @param expires 失效时间(以秒为单位),默认是7天,不得大于七天 * @return */ public String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) throws ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, InvalidExpiresRangeException { boolean flag = bucketExists(bucketName); String url = ""; if (flag) { if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) { throw new InvalidExpiresRangeException(expires, "expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME); } try { url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder() .method(Method.GET) .bucket(bucketName) .object(objectName) .expiry(expires)//动态参数 // .expiry(24 * 60 * 60)//用秒来计算一天时间有效期 // .expiry(1, TimeUnit.DAYS)//按天传参 // .expiry(1, TimeUnit.HOURS)//按小时传参数 .build()); } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidBucketNameException | InvalidExpiresRangeException | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException | XmlParserException e) { e.printStackTrace(); } } return url; } /** * 文件访问路径,这里指没有nginx代理,直接从minio自带9002端口访问的情况下 * 一般会使用nginx代理一遍,以保证和web服务的端口一致 * @param bucketName 存储桶名称 * @param objectName 存储桶里的对象名称 * @return String */ public String getObjectUrl(String bucketName, String objectName) throws ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { boolean flag = bucketExists(bucketName); String url = ""; if (flag) { try { url = minioClient.getObjectUrl(bucketName, objectName); } catch (ErrorResponseException e) { log.error("XmlParserException",e); } catch (InsufficientDataException e) { log.error("InsufficientDataException",e); } catch (InternalException e) { log.error("InternalException",e); } catch (InvalidBucketNameException e) { log.error("InvalidBucketNameException",e); } catch (InvalidKeyException e) { log.error("InvalidKeyException",e); } catch (InvalidResponseException e) { log.error("InvalidResponseException",e); } catch (IOException e) { log.error("IOException",e); } catch (NoSuchAlgorithmException e) { log.error("NoSuchAlgorithmException",e); } catch (ServerException e) { log.error("ServerException",e); } catch (XmlParserException e) { log.error("XmlParserException",e); } } return url; } }