MinIO是一款基于Go语言发开的高性能、分布式的、开源的对象存储系统。兼容亚马逊的S3协议,对Kubernetes能够友好的支持,专为AI等云原生工作负载而设计。
中文官网的下载会出现404错误,直接去英文官网进行下载即可。下载地址如下。
服务端:https://dl.minio.io/server/minio/release/windows-amd64/minio.exe
客户端:https://dl.minio.io/client/mc/release/windows-amd64/mc.exe
将安装好的服务端程序放置在D盘下的MinIO文件夹下。
打开cmd窗口输入命令
minio.exe server D:\MinIO
minio.exe是服务端程序的可执行程序,server表示启动MinIO服务器的模式,D:\MinIO表示是本地文件系统中的一个目录,MinIO服务器将在这个目录中存储数据。
在上面的D:\MinIO文件夹下有一个bucket-test文件夹就是我创建的一个bucket,其中有测试上传的数据。
启动server之后输入本地的ip加默认端口9000即可访问minio的服务端管理网页。
点击左侧导航栏的Identity下的User,然后右上角点击create user
创建好用户并且设置密码,策略选择consoleAdmin,然后点击save按钮。之后也可以使用该用户名和密码登录控制台。
创建好用户之后,点击名称进入。
依次选择Service Accounts,Create Access Key来创建accessKey和secretKey。
点击Create
再点击Download for import,保存生成的密钥。
注意: 该密钥只会展示一次,切记下载好密钥文件,或者复制粘贴到文本文档中保存。
点击左侧导航栏Buckets,再点击右上角Create Bucket
按照命名格式要求输入Bucket的名称,再点击Create Bucket按钮即可创建好Bucket。
在pom.xml中引入下面依赖
<dependency>
<groupId>io.miniogroupId>
<artifactId>minioartifactId>
<version>8.5.1version>
dependency>
定义一个minio.properties的配置文件,配置好连接minio的一些参数。
minio.endpoint=http://127.0.0.1:9000
minio.accessKey=eBocSyvafvzTwUqi
minio.secretKey=9zBut31aOFTj6MDSnFZIT4fQfR8uwq3U
minio.endpoint是连接的url
minio.accessKey和minio.secretKey是4.1.1节保存的accessKey和secretKey
定义MinioConfig类映射配置文件的参数。并且根据定义好的参数创建一个MinioClient类的Bean对象。
@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
@PropertySource(value = "classpath:minio.properties")
public class MinioConfig {
private String endpoint;
private String accessKey;
private String secretKey;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
}
定义业务接口FileService,定义上传、下载、列出对象、删除文件四个方法
public interface FileService {
/**
* 上传
*
* @param file 文件
* @param bucket bucket
*/
void upload(MultipartFile file, String bucket);
/**
* 下载
*
* @param fileName 文件名
* @param delete 删除
* @param response 响应
* @param bucket bucket
*/
void download(String bucket, String fileName, boolean delete, HttpServletResponse response);
/**
* 列出对象
*
* @param bucket bucket
* @param prefix 前缀
* @return {@link List}<{@link ObjectItem}>
*/
List<ObjectItem> listObjects(String bucket, String prefix);
/**
* 删除文件
*
* @param bucket bucket
* @param fileName 文件名称
*/
void removeFile(String bucket, String fileName);
}
定义实现类FileServiceImpl
@Service
@Slf4j
public class FileServiceImpl implements FileService {
@Resource
private MinioClient minioClient;
@Override
public void upload(MultipartFile file, String bucket) {
String fileName = FileUtils.extractUploadFileName(file);
try {
boolean exist = minioClient.bucketExists(BucketExistsArgs.builder()
.bucket(bucket)
.build());
if (!exist) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
}
InputStream inputStream = file.getInputStream();
// 上传到minio服务器
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucket)
.object(fileName)
.stream(inputStream, -1L, 10485760L)
.build());
} catch (Exception e) {
log.error(fileName + "上传失败");
}
}
@Override
public void download(String bucket, String fileName, boolean delete, HttpServletResponse response) {
try {
if (StringUtils.isBlank(fileName)) {
response.setHeader("Content-type", "text/html;charset=UTF-8");
String data = "文件下载失败";
OutputStream os = response.getOutputStream();
os.write(data.getBytes(StandardCharsets.UTF_8));
os.close();
return;
}
} catch (Exception e) {
log.error("文件名为空,下载失败!");
}
try (InputStream is = minioClient.getObject(GetObjectArgs.builder()
.bucket(bucket)
.object(fileName)
.build());
OutputStream os = response.getOutputStream()) {
byte[] buf = new byte[1024];
int length;
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 = is.read(buf)) > 0) {
os.write(buf, 0, length);
}
// 判断:下载后是否同时删除minio上的存储文件
if (BooleanUtils.isTrue(delete)) {
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(bucket)
.object(fileName)
.build());
}
} catch (Exception 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) {
e.printStackTrace();
}
}
}
@Override
public List<ObjectItem> listObjects(String bucket, String prefix) {
Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
.bucket(bucket)
.prefix(prefix)
.recursive(true)
.build());
List<ObjectItem> objectItemList = new ArrayList<>();
try {
for (Result<Item> result : results) {
Item item = result.get();
ObjectItem objectItem = new ObjectItem();
objectItem.setObjectName(item.objectName());
objectItem.setSize(item.size());
objectItemList.add(objectItem);
}
} catch (Exception e) {
log.error("查看" + bucket + "中文件对象失败");
}
return objectItemList;
}
@Override
public void removeFile(String bucket, String fileName) {
try {
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(bucket)
.object(fileName)
.build());
} catch (Exception e) {
log.error("删除[" + fileName.substring(fileName.lastIndexOf("/") + 1) + "]失败!");
}
}
}
其中upload上传文件方法中有一个FileUtils.extractUploadFileName()静态方法定义如下。
/**
* 提取上传文件名
*
* @param multipartFile 多部分文件
* @return {@link String}
*/
public static String extractUploadFileName(MultipartFile multipartFile) {
String fileName = multipartFile.getOriginalFilename();
if (fileName == null) {
throw new RuntimeException("无法获取文件名称");
}
String prefix = UUID.randomUUID().toString();
return getCurrentDatePath() + "/" + prefix + "-" + fileName;
}
/**
* 获取当前日期路径
*
* @return {@link String}
*/
private static String getCurrentDatePath() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
return sdf.format(new Date());
}
}
编写MinioController类对业务接口方法进行测试。
@RestController
@RequestMapping("/minio")
public class MinioController {
@Resource
private FileService fileService;
@PostMapping("/upload")
public void upload(MultipartFile file, String bucket) {
fileService.upload(file, bucket);
}
@GetMapping("/download")
public void download(String bucket, String fileName, boolean delete, HttpServletResponse response) {
fileService.download(bucket, fileName, delete, response);
}
@GetMapping("/listObjects")
public List<ObjectItem> listObjects(String bucket, String prefix) {
return fileService.listObjects(bucket, prefix);
}
@PostMapping("/removeFile")
public void removeFile(String bucket, String fileName) {
fileService.removeFile(bucket, fileName);
}
}
启动SpringBoot,将文件上传到刚才创建的bucket-test的Bucket中。
发送Post请求,上传文件,并填写要上传到的Bucket名称。
然后在MinIO的控制台就可以看到刚上传的文件了。
可以进行预览,也可以通过链接分享。
在上述文件中,设置的文件名称格式是:年/月/日/uuid-文件名.后缀。其中的斜杠/分割的路径会变成文件夹。
发送Get请求或者Post请求均可(但是需要注意点击Send and Download按钮,否则只会显示二进制流数据,如果是Get请求,通过浏览器的http请求也可以下载到文件),填写bucket名称,fileName以及是否下载完就删除文件标志位delete,如果下载完要删除就设置成true,不删除就设置成false。
然后会提示你保存文件路径,并显示下载成功。
在桌面即可找到下载好的图片。
剩下的列出文件和删除文件接口也是输入入参的参数即可进行相应的操作。
事实上MinioClient类中提供了很多的操作数据的方法,可以自行去研究。并且整个MinioClient所提供的方法在最新的版本中均采用建造者模式来进行使用,将一个复杂对象的构建与表示分离开来,比起之前要方便不少。