Minio-对象存储

1. 简介

官方地址

MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。

MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。

Minio使用纠删码erasure code和校验和checksum来保护数据免受硬件故障和无声数据损坏。 即便您丢失一半数量(N/2)的硬盘,您仍然可以恢复数据。

想要使数据丢失可恢复, 最少得挂载4块盘或4的倍数。

1.1 什么是纠删码erasure code?

纠删码是一种恢复丢失和损坏数据的数学算法, Minio采用Reed-Solomon code将对象拆分成N/2数据和N/2 奇偶校验块。 这就意味着如果是12块盘,一个对象会被分成6个数据块、6个奇偶校验块,你可以丢失任意6块盘(不管其是存放的数据块还是奇偶校验块),你仍可以从剩下的盘中的数据进行恢复。

其中原数据块和校验块是根据存储类型决定的。存储块设置,默认是原数据块的一半。

1.2 为什么纠删码有用?

纠删码的工作原理和RAID或者复制不同,像RAID6可以在损失两块盘的情况下不丢数据,而Minio纠删码可以在丢失一半的盘的情况下,仍可以保证数据安全。 而且Minio纠删码是作用在对象级别,可以一次恢复一个对象,而RAID是作用在卷级别,数据恢复时间很长。 Minio对每个对象单独编码,存储服务一经部署,通常情况下是不需要更换硬盘或者修复。Minio纠删码的设计目标是为了性能和尽可能的使用硬件加速。

2. 安装

2.1 docker

MinIO自定义Access和Secret密钥

要覆盖MinIO的自动生成的密钥,可以将Access和Secret密钥设为环境变量。 MinIO允许常规字符串作为Access和Secret密钥。

docker run -p 9000:9000 --name minio1 \ -e "MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE" \ -e "MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \ -v /mnt/data:/data \ -v /mnt/config:/root/.minio \ minio/minio server /data

2.2 docker-compose

version: '3.0' services: minio: image: minio/minio container_name: minio ports: - "9000:9000" restart: always command: server /data environment: MINIO_ACCESS_KEY: admin #大于等于8位 MINIO_SECRET_KEY: admin123 volumes: - /Users/ludangxin/usr/local/docker/minio/data:/data # 映射文件路径

2.3 单机多磁盘挂载

挂载多个冗余盘,防止数据丢失。

docker run -p 9000:9000 --name minio1 \ -e "MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE" \ -e "MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \ -v /mnt/data:/data1 \ -v /mnt/data:/data2 \ -v /mnt/data:/data3 \ -v /mnt/data:/data4 \ -v /mnt/config:/root/.minio \ minio/minio server /data1 /data2 /data3 /data4

2.4 分布式部署

启动一个分布式Minio实例,你只需要把硬盘位置做为参数传给minio server命令即可,然后,你需要在所有其它节点运行同样的命令。

注意

  • 分布式Minio里所有的节点需要有同样的access秘钥和secret秘钥,这样这些节点才能建立联接。为了实现这个,你需要在执行minio server命令之前,先将access秘钥和secret秘钥export成环境变量。
  • 分布式Minio使用的磁盘里必须是干净的,里面没有数据。
  • 下面示例里的IP仅供示例参考,你需要改成你真实用到的IP和文件夹路径。
  • 分布式Minio里的节点时间差不能超过3秒,你可以使用NTP 来保证时间一致。
  • 在Windows下运行分布式Minio处于实验阶段,请悠着点使用。

2.4.1 GNU/Linux 和 macOS

export MINIO_ACCESS_KEY=<ACCESS_KEY> export MINIO_SECRET_KEY=<SECRET_KEY> minio server http://192.168.1.11/export1 http://192.168.1.12/export2 \ http://192.168.1.13/export3 http://192.168.1.14/export4 \ http://192.168.1.15/export5 http://192.168.1.16/export6 \ http://192.168.1.17/export7 http://192.168.1.18/export8

2.4.2 docker-compose

部署4个节点,每个节点挂载一个盘。

version: '3.7' services: minio: image: minio/minio volumes: # 值需要换成对应节点的 - /data0:/data0 ports: - 9000:9000 environment: MINIO_ACCESS_KEY: admin MINIO_SECRET_KEY: admin123 command: minio server http://minio-00/data0 http://minio-01/data1 http://minio-02/data2 http://minio-03/data3 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 30s timeout: 20s retries: 3 # 指定当前节点的hostname hostname: minio-00 # ip地址要换成自己的 extra_hosts: - "minio-00:192.168.1.11" - "minio-01:192.168.1.12" - "minio-02:192.168.1.13" - "minio-03:192.168.1.14"

部署4个节点,每个节点四个盘。

version: '3.7' services: minio: image: minio/minio volumes: - /data0:/data0 - /data1:/data1 - /data2:/data2 - /data3:/data3 ports: - 9000:9000 environment: MINIO_ACCESS_KEY: admin MINIO_SECRET_KEY: admin123 command: minio server http://minio-0{0...3}/data{0...3} healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 30s timeout: 20s retries: 3 # 指定当前节点的hostname hostname: minio-00 # ip地址要换成自己的 extra_hosts: - "minio-00:192.168.1.11" - "minio-01:192.168.1.12" - "minio-02:192.168.1.13" - "minio-03:192.168.1.14"

2.5 访问测试

如图:访问localhost:9000,即可访问minio 登陆页面。

3. quick start

3.1 项目结构

├── pom.xml └── src └── main ├── java │   └── com │   └── ldx │   └── minio │   ├── MinioApplication.java # 启动类 │   ├── config │   │   ├── MinioConfig.java # minio 配置类 │   │   └── MinioProperties.java # minio 服务参数 │   ├── controller │   │   └── MinioController.java # 测试控制器 │   ├── test │   │   └── FileUploader.java # 测试上传 │   └── util │   └── MinioUtils.java # 工具类 └── resources └── application.yaml # 服务配置文件

3.2 引入依赖

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.3</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.ldx</groupId> <artifactId>minio</artifactId> <version>0.0.1-SNAPSHOT</version> <name>minio</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!-- springweb 支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- minio 工具包 --> <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>7.0.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>

3.2 上传测试

我们先利用测试代码测试一下 minio 附件上传功能。测试代码如下:

import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.security.InvalidKeyException; import lombok.extern.slf4j.Slf4j; import io.minio.MinioClient; import io.minio.errors.MinioException; @Slf4j public class FileUploader { public static void main(String[] args) throws NoSuchAlgorithmException, IOException, InvalidKeyException { try { // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 MinioClient minioClient = new MinioClient("http://localhost:9000", "admin", "admin123"); // 检查存储桶是否已经存在 String bucketName = "test"; boolean isExist = minioClient.bucketExists(bucketName); if(isExist) { log.info("Bucket already exists."); } else { // 创建一个名为test的存储桶,用于存储照片的zip文件。 minioClient.makeBucket(bucketName); } // 使用putObject上传一个文件到存储桶中。 minioClient.putObject(bucketName,"lion.jpg", "/Users/ludangxin/temp/舞狮.png",null); log.info("/Users/ludangxin/temp/舞狮.png is successfully uploaded as lion.jpg to `test` bucket."); } catch(MinioException e) { log.info("Error occurred: " + e); } } }

直接运行mian方法,运行成功后查看minio ui界面如下。图片上传成功,so easy~

3.3 application.yaml

minio: endpoint: http://localhost port: 9000 default-bucket-name: ldx access-key: admin secret-key: admin123

3.4 MinioProperties

import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; /** * minio 配置属性类 * * @author ludangxin * @date 2021/8/17 */ @Data @ConfigurationProperties(prefix = "minio") public class MinioProperties { /** * 端点 */ private String endpoint; /** * 端口 */ private Integer port; /** * 默认的桶名称 */ private String defaultBucketName; /** * 访问key */ private String accessKey; /** * 密钥 */ private String secretKey; }

3.5 MinioConfig

import io.minio.MinioClient; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Objects; /** * minio 配置类 * * @author ludangxin * @date 2021/8/17 */ @Slf4j @Configuration @EnableConfigurationProperties(MinioProperties.class) public class MinioConfig { @Bean @SneakyThrows public MinioClient minioClient(MinioProperties minioProperties) { MinioClient minioClient = new MinioClient(minioProperties.getEndpoint(), minioProperties.getPort(), minioProperties.getAccessKey(), minioProperties.getSecretKey()); String defaultBucketName = minioProperties.getDefaultBucketName(); if(Objects.nonNull(defaultBucketName)) { // 创建默认的bucket if(!minioClient.bucketExists(defaultBucketName)) { log.info("create default bucket \"{}\" success", defaultBucketName); minioClient.makeBucket(defaultBucketName); } } return minioClient; } }

3.6 MinioController

import com.ldx.minio.util.MinioUtils; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; /** * minio 控制器 * * @author ludangxin * @date 2021/8/18 */ @RestController @RequestMapping("minio") @RequiredArgsConstructor public class MinioController { private final MinioUtils minioUtils; @GetMapping public String getUrl(String fileName) { return minioUtils.getFileUrl(fileName); } @PostMapping public String upload(MultipartFile file) { return minioUtils.upload(file); } @GetMapping("download/{fileName}") public void download(@PathVariable String fileName, HttpServletResponse response) { minioUtils.download(fileName, response); } @DeleteMapping public void del(String fileName) { minioUtils.delFile(fileName); } }

3.7 MinioUtils

package com.ldx.minio.util; import io.minio.MinioClient; import io.minio.PutObjectOptions; import io.minio.messages.Bucket; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.tomcat.util.http.fileupload.FileUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; 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.nio.charset.StandardCharsets; import java.util.List; import java.util.Objects; /** * minio 工具类 * * @author ludangxin * @date 2021/8/17 */ @Slf4j @Component @RequiredArgsConstructor public class MinioUtils { @Value("${minio.default-bucket-name}") private String defaultBucketName; private final MinioClient minioClient; /** * 获取全部bucket * * @return all bucket */ @SneakyThrows public List<Bucket> getAllBuckets() { return minioClient.listBuckets(); } /** * 判断 bucket是否存在 * * @param bucketName 桶名称 * @return true 存在 */ @SneakyThrows public boolean bucketExists(String bucketName){ return minioClient.bucketExists(bucketName); } /** * 创建 bucket * * @param bucketName 桶名称 */ @SneakyThrows public void createBucket(String bucketName){ boolean isExist = minioClient.bucketExists(bucketName); if(!isExist) { minioClient.makeBucket(bucketName); } } /** * 文件上传 * * @param bucketName 桶名称 * @param fileName 上传后的文件名称 * @param fileAbsolutePath 文件的绝对路径 * @return 文件url */ @SneakyThrows public String upload(String bucketName, String fileName, String fileAbsolutePath){ minioClient.putObject(bucketName, fileName, fileAbsolutePath,null); return getFileUrl(bucketName, fileName); } /** * 文件上传 * * @param fileName 上传后的文件名称 * @param stream 文件输入流 * @return 文件url */ @SneakyThrows public String upload(String fileName, InputStream stream){ this.upload(defaultBucketName, fileName, stream); return getFileUrl(defaultBucketName, fileName); } /** * 文件上传 * * @param file 文件 * @return 文件url */ public String upload(MultipartFile file) { final String fileName = file.getOriginalFilename(); this.upload(defaultBucketName, file); return this.getFileUrl(defaultBucketName, fileName); } /** * 文件上传 * * @param bucketName 桶名称 * @param file 文件 * @return 文件url */ public String upload(String bucketName, MultipartFile file) { InputStream is = null; try { is = file.getInputStream(); final String fileName = file.getOriginalFilename(); this.upload(bucketName, fileName, is); return getFileUrl(bucketName, fileName); } catch(Exception e) { log.error(e.getMessage()); } finally { try { if(Objects.nonNull(is)) { is.close(); } } catch(IOException e) { log.error(e.getMessage()); } } return null; } /** * 文件上传 * * @param bucketName 桶名称 * @param fileName 上传后的文件名称 * @param stream 文件输入流 * @return 文件url */ @SneakyThrows public String upload(String bucketName, String fileName, InputStream stream){ minioClient.putObject(bucketName, fileName, stream, new PutObjectOptions(stream.available(), -1)); return getFileUrl(bucketName, fileName); } /** * 附件下载 * * @param fileName 附件名称 */ @SneakyThrows public void download(String fileName, HttpServletResponse response) { this.download(defaultBucketName, fileName, response); } /** * 文件下载 * * @param bucketName 桶名称 * @param fileName 文件名称 */ public void download(String bucketName, String fileName, HttpServletResponse response) { InputStream in = null; OutputStream out = null; try { in = minioClient.getObject(bucketName, fileName); int len = 0; byte[] buffer = new byte[1024]; out = response.getOutputStream(); response.reset(); response.addHeader("Content-Disposition", " attachment;filename=" + new String(fileName.getBytes(), StandardCharsets.ISO_8859_1)); response.setContentType("application/octet-stream"); while ((len = in.read(buffer)) > 0) { out.write(buffer, 0, len); } } catch (Exception e) { log.error(e.getMessage()); } finally { if (in != null){ try { in.close(); } catch (Exception e) { log.error(e.getMessage()); } } if (out != null) { try { out.close(); } catch (IOException e) { log.error(e.getMessage()); } } } } /** * 删除文件 * * @param fileName 文件名称 */ @SneakyThrows public void delFile(String fileName){ this.delFile(defaultBucketName, fileName); } /** * 删除文件 * * @param bucketName 桶名称 * @param fileName 文件名称 */ @SneakyThrows public void delFile(String bucketName, String fileName){ minioClient.removeObject(bucketName, fileName); } /** * 获取minio文件的下载地址 * * @param fileName 文件名 */ @SneakyThrows public String getFileUrl(String fileName) { return this.getFileUrl(defaultBucketName, fileName); } /** * 获取minio文件的下载地址 * * @param bucketName 桶名称 * @param fileName 文件名 */ @SneakyThrows public String getFileUrl(String bucketName, String fileName) { return minioClient.presignedGetObject(bucketName, fileName); } /** * 设置桶策略 * * @param bucketName 桶名称 * @param policy 策略 */ @SneakyThrows public void setBucketPolicy(String bucketName, String policy) { minioClient.setBucketPolicy(bucketName, policy); } }

4. 最新版本工具包

在当前最新的jar中(8.3.0),minio 工具类的使用方法跟7.0.2版本还是有很大出入的,在这里记录一下。

4.1 MinioConfig

import io.minio.BucketExistsArgs; import io.minio.MakeBucketArgs; import io.minio.MinioClient; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Objects; /** * minio 配置类 * * @author ludangxin * @date 2021/8/17 */ @Slf4j @Configuration @EnableConfigurationProperties(MinioProperties.class) public class MinioConfig { @Bean @SneakyThrows public MinioClient minioClient(MinioProperties minioProperties) { MinioClient minioClient = MinioClient.builder() .endpoint(minioProperties.getEndpoint(), minioProperties.getPort(), false) .credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey()) .build(); String defaultBucketName = minioProperties.getDefaultBucketName(); if(Objects.nonNull(defaultBucketName)) { BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder() .bucket(defaultBucketName) .build(); // 创建默认的bucket if(!minioClient.bucketExists(bucketExistsArgs)) { MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder() .bucket(defaultBucketName) .build(); minioClient.makeBucket(makeBucketArgs); log.info("create default bucket \"{}\" success", defaultBucketName); } } return minioClient; } }

4.2 MinioUtils

import io.minio.*; import io.minio.http.Method; import io.minio.messages.Bucket; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; 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.nio.charset.StandardCharsets; import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; /** * minio 工具类 * * @author ludangxin * @date 2021/8/17 */ @Slf4j @Component @RequiredArgsConstructor public class MinioUtils { @Value("${minio.default-bucket-name}") private String defaultBucketName; private final MinioClient minioClient; /** * 获取全部bucket * * @return all bucket */ @SneakyThrows public List<Bucket> getAllBuckets() { return minioClient.listBuckets(); } /** * 判断 bucket是否存在 * * @param bucketName 桶名称 * @return true 存在 */ @SneakyThrows public boolean bucketExists(String bucketName){ BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder() .bucket(bucketName) .build(); return minioClient.bucketExists(bucketExistsArgs); } /** * 创建 bucket * * @param bucketName 桶名称 */ @SneakyThrows public void createBucket(String bucketName){ boolean isExist = this.bucketExists(bucketName); if(!isExist) { MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder() .bucket(bucketName) .build(); minioClient.makeBucket(makeBucketArgs); } } /** * 文件上传 * * @param bucketName 桶名称 * @param fileName 上传后的文件名称 * @param fileAbsolutePath 文件的绝对路径 * @return 文件url */ @SneakyThrows public ObjectWriteResponse upload(String bucketName, String fileName, String fileAbsolutePath){ UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder() .bucket(bucketName) .filename(fileAbsolutePath) .object(fileName) .build(); return minioClient.uploadObject(uploadObjectArgs); } /** * 文件上传 * * @param fileName 上传后的文件名称 * @param stream 文件输入流 * @return 文件url */ @SneakyThrows public String upload(String fileName, InputStream stream){ this.upload(defaultBucketName, fileName, stream); return getFileUrl(defaultBucketName, fileName); } /** * 文件上传 * * @param bucketName 桶名称 * @param fileName 上传后的文件名称 * @param stream 文件输入流 * @return 文件url */ @SneakyThrows public ObjectWriteResponse upload(String bucketName, String fileName, InputStream stream){ try { PutObjectArgs objectArgs = PutObjectArgs.builder() .bucket(bucketName) .object(fileName) .stream(stream, stream.available(), -1) .build(); return minioClient.putObject(objectArgs); } catch(Exception e) { log.error(e.getMessage()); } finally { try { if(Objects.nonNull(stream)) { stream.close(); } } catch(IOException e) { log.error(e.getMessage()); } } return null; } /** * 文件上传 * * @param file 文件 * @return 文件url */ public ObjectWriteResponse upload(MultipartFile file) { return this.upload(defaultBucketName, file); } /** * 文件上传 * * @param bucketName 桶名称 * @param file 文件 * @return 文件url */ public ObjectWriteResponse upload(String bucketName, MultipartFile file) { InputStream is = null; try { is = file.getInputStream(); final String fileName = file.getOriginalFilename(); String contentType = file.getContentType(); PutObjectArgs objectArgs = PutObjectArgs.builder() .bucket(bucketName) .object(fileName) .stream(is, is.available(), -1) .contentType(contentType) .build(); return minioClient.putObject(objectArgs); } catch(Exception e) { log.error(e.getMessage()); } finally { try { if(Objects.nonNull(is)) { is.close(); } } catch(IOException e) { log.error(e.getMessage()); } } return null; } /** * 附件下载 * * @param fileName 附件名称 */ @SneakyThrows public void download(String fileName, HttpServletResponse response) { this.download(defaultBucketName, fileName, response); } /** * 附件下载 * * @param bucketName 桶名称 * @param fileName 附件名称 */ @SneakyThrows public void download(String bucketName, String fileName, HttpServletResponse response) { GetObjectArgs build = GetObjectArgs.builder().bucket(bucketName).object(fileName).build(); OutputStream out = null; try(GetObjectResponse object = minioClient.getObject(build)) { int len = 0; byte[] buffer = new byte[1024]; out = response.getOutputStream(); response.reset(); String fileName1 = new String(fileName.getBytes(), StandardCharsets.ISO_8859_1); response.addHeader("Content-Disposition", " attachment;filename=" + fileName1); response.setContentType("application/octet-stream"); while((len = object.read(buffer)) > 0) { out.write(buffer, 0, len); } } catch(Exception e) { log.error(e.getMessage()); } finally { if(out != null) { try { out.close(); } catch(IOException e) { log.error(e.getMessage()); } } } } /** * 附件下载 * * @param fileName 附件名称 */ @SneakyThrows public void download(String bucketName, String fileName, String fileAbsolutePath) { DownloadObjectArgs downloadObjectArgs = DownloadObjectArgs.builder() .bucket(bucketName) .object(fileName) .filename(fileAbsolutePath) .build(); minioClient.downloadObject(downloadObjectArgs); } /** * 删除文件 * * @param fileName 文件名称 */ @SneakyThrows public void delFile(String fileName){ this.delFile(defaultBucketName, fileName); } /** * 删除文件 * * @param bucketName 桶名称 * @param fileName 文件名称 */ @SneakyThrows public void delFile(String bucketName, String fileName){ RemoveObjectArgs removeObjectsArgs = RemoveObjectArgs.builder() .bucket(bucketName) .object(fileName) .build(); minioClient.removeObject(removeObjectsArgs); } /** * 获取minio文件的下载地址 * * @param fileName 文件名 */ @SneakyThrows public String getFileUrl(String fileName) { return this.getFileUrl(defaultBucketName, fileName); } /** * 获取minio文件的下载地址 * * @param bucketName 桶名称 * @param fileName 文件名 */ @SneakyThrows public String getFileUrl(String bucketName, String fileName) { GetPresignedObjectUrlArgs objectUrlArgs = GetPresignedObjectUrlArgs.builder() .method(Method.GET) .bucket(bucketName) .object(fileName) .build(); return minioClient.getPresignedObjectUrl(objectUrlArgs); } /** * 获取minio文件的下载地址 * * @param fileName 文件名 */ @SneakyThrows public String getFileUrl(String fileName, Integer duration, TimeUnit unit) { return this.getFileUrl(defaultBucketName, fileName, duration, unit); } /** * 获取minio文件的下载地址 * * @param bucketName 桶名称 * @param fileName 文件名 */ @SneakyThrows public String getFileUrl(String bucketName, String fileName, Integer duration, TimeUnit unit) { GetPresignedObjectUrlArgs objectUrlArgs = GetPresignedObjectUrlArgs.builder() .method(Method.GET) .bucket(bucketName) .object(fileName) .expiry(duration, unit) .build(); return minioClient.getPresignedObjectUrl(objectUrlArgs); } /** * 设置桶策略 * * @param bucketName 桶名称 * @param policy 策略 */ @SneakyThrows public void setBucketPolicy(String bucketName, String policy) { SetBucketPolicyArgs bucketPolicyArgs = SetBucketPolicyArgs.builder() .bucket(bucketName) .config(policy) .build(); minioClient.setBucketPolicy(bucketPolicyArgs); } }

4.3 测试类

package com.ldx.minio.controller; import com.ldx.minio.util.MinioUtils; import io.minio.ObjectWriteResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; /** * minio 控制器 * * @author ludangxin * @date 2021/8/18 */ @RestController @RequestMapping("minio") @RequiredArgsConstructor public class MinioController { private final MinioUtils minioUtils; @GetMapping public String getUrl(String fileName) { return minioUtils.getFileUrl(fileName); } @PostMapping public ObjectWriteResponse upload(MultipartFile file) { return minioUtils.upload(file); } @GetMapping("download/{fileName}") public void download(@PathVariable String fileName, HttpServletResponse response) { minioUtils.download(fileName, response); } @GetMapping("download/{bucketName}/{fileName}") public void download(@PathVariable String bucketName, @PathVariable String fileName) { minioUtils.download(bucketName, fileName, "/Users/ludangxin/temp/" + fileName); } @DeleteMapping public void del(String fileName) { minioUtils.delFile(fileName); } }

__EOF__

本文作者张铁牛
本文链接https://www.cnblogs.com/ludangxin/p/15178147.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   张铁牛  阅读(6320)  评论(1编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示