docker run --name minio \
-d --restart=always \
-p 9000:9000 \
-p 9001:9001 \
--network tool-net --network-alias minio \
-e "MINIO_ACCESS_KEY=admin" \
-e "MINIO_SECRET_KEY=admin123456" \
-v minio_data:/data \
-v min_config:/root/.minio \
minio/minio server /data --console-address ":9001"
注意:
--console-address
当前最新版必须指定控制台端口(浏览器页面访问端口),如果不指定,将会随机生成端口,不方便服务器开启防火墙--console-address
进入控制台可以通过9000端口,他会自动跳转到控制台的9001端口,也可以直接通过9001端口进入控制台
控台登录账户和密码是Docker 启动时的MINIO_ACCESS_KEY
参数和MINIO_SECRET_KEY
参数
桶(Bucket):Minio 通过桶去存储文件,存储文件时需要指定目标桶才可以存储,
点击Bucket->create bucket
右侧有关于配置信息的解释
Retention
,那么文件将不能被删除Object locking
会自动开启,因为文件保留期间,对象是禁止删除的
Validity
天后被删除 2. Governance 在Validity
天后删除在生命周期内未操作的对象点击Bucket-->Manage--> Access Rules
上传后的对象名格式:
[Rule alias]_[Object Name].[文件后缀]
身份管理
创建、管理Minio账户
对Minio用户进行分组
创建、管理服务账户
这里创建的账户,主要用于开发,也是最常用的
create service account
,创建Service AccountMinio会自动创建键账户和密码,如果开发这需要配置使用权限,可开启下方的Restrict beyond user policy
<dependency>
<groupId>io.miniogroupId>
<artifactId>minioartifactId>
<version>8.2.1version>
dependency>
Minio 参数 Properties类
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "minio")
public class MinIOProperties {
/**
* 连接url
*/
private String endpoint;
/**
* Service Account——accesskey
*/
private String accessKey;
/**
* Service Account——secretKey
*/
private String secretKey;
/**
* 桶
*/
private String bucket;
/**
* 分片大小
*/
private long partSize=5 * 1024 * 1024;
}
Minio工具类,用于编写操作Minio的具体方法
该类在AutoConfig类中已注入到IOC容器中,方便开发者在service或controller中调用
import cn.hutool.core.util.ObjectUtil;
import io.minio.*;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import java.io.FileNotFoundException;
public class MinIOHelper {
@Autowired
private MinioClient client;
@Autowired
private MinIOProperties properties;
/**
* 创建桶
*
* @param bucketName 通名称
*/
@SneakyThrows
public void createBucket(String bucketName) {
BucketExistsArgs bucket = BucketExistsArgs.builder().bucket(bucketName).build();
if (!client.bucketExists(bucket)) {
MakeBucketArgs make = MakeBucketArgs.builder().bucket(bucketName).build();
client.makeBucket(make);
}
}
/**
* 上传文件
*
* @param file 文件
* @param bucketName 存储桶
* @return
*/
public String uploadFile(MultipartFile file, String bucketName, FileRule rule) throws FileNotFoundException {
// 判断上传文件是否为空
if (ObjectUtil.isEmpty(file) || 0 == file.getSize()) {
throw new FileNotFoundException();
}
//检查桶名
if (ObjectUtil.isEmpty(bucketName)) {
bucketName = properties.getBucket();
if (ObjectUtil.isEmpty(bucketName)) {
throw new MinioUploadExecption("Bucket 必填");
}
}
if (ObjectUtil.isEmpty(rule)) {
rule = FileRule.READ_WRITE;
}
try {
// 判断存储桶是否存在
createBucket(bucketName);
// 文件名
String originalFilename = file.getOriginalFilename();
// 新的文件名 = 存储桶名称_时间戳.后缀名
String fileName = rule.getLabel() + "_" + System.currentTimeMillis() + originalFilename.substring(originalFilename.lastIndexOf("."));
// 开始上传
PutObjectArgs build = PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.contentType(file.getContentType())
.stream(file.getInputStream(), file.getSize(), properties.getPartSize())
.build();
ObjectWriteResponse response = client.putObject(build);
return bucketName + "/" + fileName;
} catch (Exception e) {
throw new MinioUploadExecption("上传失败");
}
}
}
该枚举类实现的是Bucket Access Rules配置内容,方便开发者调用,不在用于记忆前缀
public enum FileRule {
WRITE_ONLY(0, "w"),
READ_ONLY(1, "r"),
READ_WRITE(2,"r-w");
private Integer value;
private String label;
FileRule(Integer value, String label) {
this.value = value;
this.label = label;
}
public Integer getValue() {
return value;
}
public String getLabel() {
return label;
}
}
Minio启动注解
将该类配置在对应服务的启动类上,即可
服务启动时spring 会自动去寻找@Import({MinIOAutoCondig.class})
的自动配置类,从而实现自动配置
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({MinIOAutoCondig.class})
public @interface EnableOSS {
}
Minio 自动配置类
因为Minio 本身就是未分布式微服务而设计的,所以必然存在多模块,而为了调用方便,所以本文使用启动注解加自动配置类的方式调用Minio
import cn.hutool.core.util.ObjectUtil;
import io.minio.MinioClient;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ConditionalOnClass({MinioClient.class})
@Configuration
@EnableConfigurationProperties({MinIOProperties.class})
public class MinIOAutoCondig {
@Autowired
private MinIOProperties minIOProperties;
private MinioClient client;
@Bean
public MinioClient minioClient() {
if (ObjectUtil.isEmpty(minIOProperties.getEndpoint())) {
throw new BeanCreationException("minio.endpoint don't null");
}
if (ObjectUtil.isEmpty(minIOProperties.getAccessKey())) {
throw new BeanCreationException("minio.access-key don't null");
}
if (ObjectUtil.isEmpty(minIOProperties.getSecretKey())) {
throw new BeanCreationException("minio.secret-key don't null");
}
client= MinioClient.builder().endpoint(minIOProperties.getEndpoint()).credentials(minIOProperties.getAccessKey(), minIOProperties.getSecretKey()).build();
return client;
}
@Bean
public MinIOHelper minIOHelper(){
return new MinIOHelper();
}
}
minio.access-key
、minio.secret-key
分别对应的是Services Accounts 中创建的账户
minio.endpoint=http://xx.xx.xx.xx:9000
minio.bucket=client
minio.access-key=gutgSnXxxxxH2zgxxx
minio.secret-key=EOiOz9KFxxxxmaC8uGLxxxxQlJINVPSxxx
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import top.cxjfun.jdoc.client.server.vo.UploadFile;
import top.cxjfun.jdoc.common.CustomException;
import top.cxjfun.jdoc.common.db.oss.MinIOHelper;
import javax.servlet.http.HttpServletRequest;
import java.io.FileNotFoundException;
@RestController
@RequestMapping("/file")
public class FileController {
@Autowired
private MinIOHelper minIOHelper;
@PostMapping("/upload")
@ResponseBody
public String upload(MultipartFile file, HttpServletRequest request) {
try {
String filePath = minIOHelper.uploadFile(file, null, null);
return filePath;
} catch (FileNotFoundException e) {
throw new CustomException("FILE_NOT_FOUND", "文件未找到");
}
}
}
An attempt was made to call a method that does not exist. The attempt was made from the following location:
io.minio.S3Base.<clinit>(S3Base.java:105)
The following method did not exist:
okhttp3.RequestBody.create([BLokhttp3/MediaType;)Lokhttp3/RequestBody;
The method's class, okhttp3.RequestBody, is available from the following locations:
jar:file:/D:/repository/com/squareup/okhttp3/okhttp/3.14.9/okhttp-3.14.9.jar!/okhttp3/RequestBody.class
The class hierarchy was loaded from the following locations:
okhttp3.RequestBody: file:/D:/repository/com/squareup/okhttp3/okhttp/3.14.9/okhttp-3.14.9.jar
Action:
Correct the classpath of your application so that it contains a single, compatible version of okhttp3.RequestBody
Spring boot 和minio 依赖冲突产生的原因是因为spring boot 的okhttp 和minio 的okhttp冲突
降低minio版本:8.3.0 —> 8.2.1