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);
} }

minio-对象存储的更多相关文章

  1. Minio对象存储

    目录 Minio对象存储 1.概述 2.功能特性 3.2.多节点 3.3.分布式 4.分布式minio集群搭建 4.1.集群规划 4.3.编写集群启动脚本(所有节点) 4.4.编写服务脚本(所有节点) ...

  2. 在kubernetes中搭建harbor,并利用MinIO对象存储保存镜像文件

    前言:此文档是用来在线下环境harbor利用MinIO做镜像存储的,至于那些说OSS不香吗?或者单机harbor的,不用看了.此文档对你没啥用,如果是采用单机的harbor连接集群MinIO,请看我的 ...

  3. 轻量对象存储服务——minio

    minio Minio是一个非常轻量的对象存储服务. Github: minio 它本身不支持文件的版本管理.如果有这个需求,可以用 s3git 搭配使用. Github: s3git 安装 mini ...

  4. 对象存储服务-Minio

    Mino 目录 Mino 对象存储服务 Minio 参考 Minio 架构 为什么要用 Minio 存储机制 纠删码 MinIO概念 部署 单机部署: Docker 部署Minio 分布式Minio ...

  5. 对象存储服务MinIO安装部署分布式及Spring Boot项目实现文件上传下载

    目录 一.MinIO快速入门 1. MinIO简介 2. CentOS7更换成阿里云镜像 3. 安装 3.1 下载 3.2 运行测试 4. 配置脚本执行文件 4.1 创建配置执行文件 4.2 执行 二 ...

  6. 免费版对象存储【minIO】CentOS部署实践记录 2021

    好久没写,记录一下 1.背景 之前一直用的七牛,不过是收费的,然后有些定制化需求,可能比较看重预算,然后就有了这篇开源方式:minio 2.简介 官方文档:http://docs.minio.org. ...

  7. Github 29K Star的开源对象存储方案——Minio入门宝典

    对象存储不是什么新技术了,但是从来都没有被替代掉.为什么?在这个大数据发展迅速地时代,数据已经不单单是简单的文本数据了,每天有大量的图片,视频数据产生,在短视频火爆的今天,这个数量还在增加.有数据表明 ...

  8. 使用MinIO搭建对象存储服务

    1.MinIO是什么? MinIO 是一款高性能.分布式的对象存储系统. 它是一款软件产品, 可以100%的运行在标准硬件.即X86等低成本机器也能够很好的运行MinIO. MinIO与传统的存储和其 ...

  9. [转载] 对象存储(2):OpenStack Swift——概念、架构与规模部署

    原文: http://www.testlab.com.cn/Index/article/id/1085.html#rd?sukey=fc78a68049a14bb228cb2742bdec2b9498 ...

  10. [转载] 文件系统vs对象存储——选型和趋势

    原文: http://www.testlab.com.cn/Index/article/id/1082.html#rd?sukey=fc78a68049a14bb2699b479d5e730f6f45 ...

随机推荐

  1. Java实验项目三——宠物商店

    Program:宠物商店的设计(继承,接口,线性线性表) Description:本题未实现图形用户界面,项目结构描述如下: classes.Pet:定义宠物接口,只要实现该接口的宠物类,都可存储进宠 ...

  2. XCTF-boomshakalaka-3

    这题讲道理出的挺脑洞的,apk直接拖入jeb打开,找到主活动 这里有两个方法,一个是onCreate()方法,还有一个是onCreateView()方法 onCreate()方法调用了a这个对象的d方 ...

  3. 递推算法,AI衍生

    引言 最近在刷leetcode算法题的时候,51题很有意思: 题目是这样的: n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击.给你一个整数 n ,返回 ...

  4. C语言:指针

    #include <stdio.h> #include <stdlib.h> int sum(int a,int b) { int c; c=a+b; printf(" ...

  5. spring boot(二)整合mybatis plus+ 分页插件 + 代码生成

    先创建spring boot项目,不知道怎么创建项目的 可以看我上一篇文章 用到的环境 JDK8 .maven.lombok.mysql 5.7 swagger 是为了方便接口测试 一.Spring ...

  6. VSCode 使用

    运行时,如何弹出cmd命令窗口:将launch.json文件中的 externalConsole设置为true,并按F5运行(不要按右上角的运行按钮) 如何cin:先再命令窗口通过g++ *.cpp生 ...

  7. SSM中如何上传图片

    1.文件配置 2.jsp页面 文件的name值不能跟数据库列名一致 3.控制层收集数据转发到逻辑层 4.逻辑层处理把用户信息存到数据库 5.注册成功后跳到jsp页面进行展示

  8. Java逻辑运算符&与&&

    & 和&&的区别 && 短路与 ,一个条件不成立,跳出判断 & 与 , 全部判断 boolean b1 = false; int num = 9; if ...

  9. jvm源码解读--18 Java的start()方法解读 以及 wait 和notify流程图

    drawwed by 张艳涛 and get info from openjdk8 还有一个图

  10. Adobe ColdFusion 文件读取漏洞(CVE-2010-2861)

    影响范围 Adobe ColdFusion 8.9版本中存在一处目录穿越漏洞 poc http://192.168.49.2:8500/CFIDE/administrator/enter.cfm?lo ...