minio使用
简单描述
minIO是一个分布式的对象存储系统,它提供了与Amazon Web Service S3兼容的API,并支持所有核心S3功能。
minIO可以部署在裸机环境,即X86等低成本机器等。
单机部署
安装minio服务
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
sudo mv minio /usr/local/bin/
启动minio
#在家目录创建文件存储路径
mkdir ~/minio
#使用默认端口启动minio
minio server ~/minio
#设置控制台端口启动minio
minio server ~/minio --console-address :9090
#修改minio的初始账号和密码,并设置控制台端口启动minio
MINIO_ROOT_USER=admin MINIO_ROOT_PASSWORD=password ./minio server ~/minio --console-address :9090
#修改minio的初始账号和密码
export MINIO_ROOT_USER=minio
export MINIO_ROOT_PASSWORD=12345678
备注:密码至少八位
浏览器访问minio服务
http://ip:9090
minio部署后台运行
使用systemd service功能管理minio服务的启动和停止等。
创建配置文件
mkdir -p /opt/minio/config
touch minio.config
创建minio.conf
在/opt/minio/conf目录下新建minion.conf,输入以下内容:
MINIO_VOLUMES="/opt/minio/data"
MINIO_OPTS="--console-address :9090 --address :9000"
MINIO_ROOT_USER="minio"
MINIO_ROOT_PASSWORD="12345678"
创建minio.service
在/etc/systemd/system目录下新建一个minio.service,输入内容:
[Unit]
Description=minio
Documentation=https://docs.min.io
Wants=network-online.target
After=network-online.target
AssertFileIsExecutable=/opt/minio/minio
[Service]
#User and group
User=root
Group=root
#创建的配置文件 minio.conf
EnvironmentFile=/opt/minio/config/minio.conf
ExecStart=/opt/minio/minio server $MINIO_OPTS $MINIO_VOLUMES
#Let systemd restart this service always
Restart=always
#Specifies the maximum file descriptor number that can be opened by this process
LimitNOFILE=65536
#Disable timeout logic and wait until process is stopped
TimeoutStopSec=infinity
SendSIGKILL=no
[Install]
WantedBy=multi-user.target
修改系统服务
(1)将服务设置为每次开机启动
systemctl enable minio.service
(2)重新加载服务的配置文件
如果新安装的服务归属于 systemctl 管理,要使新服务的配置文件生效,需重新加载。
systemctl deamon-reload
(3)启动服务
systemctl start minio
(4)停止服务
systemctl stop minio
(5)重启服务
systemctl restart minio
(6)查看服务状态
systemctl status minio
springboot整合minio
引入依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.4.3</version>
</dependency>
springboot minio client配置
package com.ym.minio.config;
import com.ym.minio.base.config.MinioProperty;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MinioConfig {
@Autowired
private MinioProperty minioProperties;
@Bean
public MinioClient minioClient(){
MinioClient minioClient =
MinioClient.builder()
.endpoint(minioProperties.getUrl())
.credentials(minioProperties.getAccessKey(),
minioProperties.getSecretKey())
.build();
return minioClient;
}
}
## springboot minio属性配置
minio:
bucketName: bucket001
expiry: 24
url: http://192.168.117.129:9000
accesskey: minio
secretKey: 12345678
java api查看
postman请求接口
MinioService.java
package com.ym.minio.service;
import com.ym.minio.base.response.BaseResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
public interface MinioService {
public static final String KEY_FILENAME = "fileName";
public static final String KEY_PRESIGNEDOBJECTURL = "presignedObjectUrl";
public static final String KEY_BUCKETNAME = "bucketName";
public static final String KEY_FILEINFO = "fileInfo";
public static final String KEY_FILELIST = "fileList";
public static final String FILESIZE_0B = "0B";
public BaseResponse<Map<String, Object>> createBucket(String bucketName);
public BaseResponse<Map<String, Object>> delBucket(String bucketName);
public BaseResponse<List<Map<String, Object>>> getBucketList();
public BaseResponse<Map<String, Object>> getPresignedObjectUrl(String bucket, String fileName);
public BaseResponse<String> delete(String bucket, String fileName);
public void download(HttpServletResponse response, String bucket, String dir, String fileName);
public ResponseEntity<byte[]> downloadByte(String bucket, String fileName);
public BaseResponse<Map<String, Object>> upload(String bucket, String dir, MultipartFile[] file);
public BaseResponse<Map<String, List<Object>>> list(String bucket);
public BaseResponse<String> downloadObject(String bucket, String objName, String fileName);
}
MinioServiceImpl.java
package com.ym.minio.service.impl;
import com.alibaba.fastjson.JSON;
import com.ym.minio.base.config.MinioProperty;
import com.ym.minio.base.constant.CommonErrorCode;
import com.ym.minio.base.constant.MinioBucketHandle;
import com.ym.minio.base.response.BaseResponse;
import com.ym.minio.service.MinioService;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class MinioServiceImpl implements MinioService {
@Autowired
private MinioClient minioClient;
@Autowired
private MinioProperty minioProperty;
@Autowired
private MinioUtil minioUtil;
public BaseResponse<Map<String, Object>> createBucket(String bucketName) {
int state = minioUtil.makeBucket(bucketName);
if (state == MinioBucketHandle.BUCKETCREATED_SUCCESS.getState()) {
return BaseResponse.SUCCESSFUL();
} else {
return BaseResponse.FAILED();
}
}
public BaseResponse<Map<String, Object>> delBucket(String bucketName) {
int state = minioUtil.removeBucket(bucketName);
if (state == MinioBucketHandle.BUCKETDEL_SUCCESS.getState()) {
return BaseResponse.SUCCESSFUL();
} else {
return BaseResponse.FAILED();
}
}
public BaseResponse<List<Map<String, Object>>> getBucketList() {
try {
List<Bucket> buckets = minioClient.listBuckets();
List<Map<String, Object>> result = new ArrayList<>();
if (null != buckets && buckets.size() > 0) {
for (Bucket bucket : buckets) {
Map<String, Object> map = new HashMap<>();
map.put("name", bucket.name());
result.add(map);
}
return BaseResponse.success(result);
} else {
return BaseResponse.success(result);
}
} catch (Exception e) {
log.error("getBucketList,e:{}", e);
return BaseResponse.FAILED();
}
}
public BaseResponse<Map<String, Object>> getPresignedObjectUrl(String bucket, String fileName) {
try {
String presignedObjectUrl = minioUtil.getPresignedObjectUrl(bucket, fileName, minioProperty.getExpiry());
Map<String, Object> result = new HashMap<>();
result.put(MinioService.KEY_PRESIGNEDOBJECTURL, presignedObjectUrl);
return BaseResponse.success(result);
} catch (Exception e) {
log.error("getPresignedObjectUrl,e:{}",e);
return BaseResponse.FAILED();
}
}
public BaseResponse<String> delete(String bucket, String fileName) {
try {
GetObjectArgs getObjectArgs = GetObjectArgs.builder().bucket(bucket).object(fileName).build();
GetObjectResponse object = minioClient.getObject(getObjectArgs);
if (null == object) {
log.error("delete,fileName not exist");
return BaseResponse.FAILED();
} else {
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(fileName).build());
}
} catch (Exception e) {
log.error("delete,e:{}", e);
return BaseResponse.FAILED();
}
return BaseResponse.SUCCESSFUL();
}
@Override
public void download(HttpServletResponse response, String bucket, String dir, String fileName) {
String path = "";
if (!StringUtils.isEmpty(dir)) {
path = dir;
}
InputStream in = null;
try {
// 获取对象信息
StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder().bucket(bucket).object(fileName).build());
response.setContentType(stat.contentType());
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
//文件下载
in = minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucket)
.object(path + fileName)
.build());
IOUtils.copy(in, response.getOutputStream());
} catch (Exception e) {
log.error("download,e1:{}", e.getMessage());
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
log.error("download,e2:{}", e);
}
}
}
}
@Override
public ResponseEntity<byte[]> downloadByte(String bucket, String fileName) {
InputStream in = null;
try {
//文件下载
in = minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucket)
.object(fileName)
.build());
byte[] bytes = IOUtils.toByteArray(in);
return new ResponseEntity(bytes, null, HttpStatus.OK);
} catch (Exception e) {
log.error("download,e1:{}", e.getMessage());
return new ResponseEntity(null, null, HttpStatus.OK);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
log.error("download,e2:{}", e);
}
}
}
}
public BaseResponse<Map<String, Object>> upload(String bucket, String dir, MultipartFile[] file) {
if (file == null || file.length == 0) {
return BaseResponse.error(CommonErrorCode.FAILED, "上传文件为空", null);
}
try {
List<Map<String, Object>> stringList = minioUtil.putObject(dir, file, bucket);
Map<String, Object> data = new HashMap<String, Object>();
data.put(MinioService.KEY_BUCKETNAME, bucket);
data.put(MinioService.KEY_FILELIST, stringList);
return BaseResponse.success(data);
} catch (Exception e) {
log.error("upload,e:{}", e);
}
return BaseResponse.FAILED();
}
public BaseResponse<Map<String, List<Object>>> list1(String buckent) {
try {
minioUtil.makeBucket(minioProperty.getBucketName());
List<Object> items = new ArrayList<>();
String format = "{'fileName':'%s','fileSize':'%s', 'uploadedTime':'%s', 'resignedObjectUrl':'%s'}";
getList(format, items, buckent, "");
Map<String, List<Object>> fileList = new HashMap<>();
fileList.put(MinioService.KEY_FILELIST, items);
return BaseResponse.success(fileList);
} catch (Exception e) {
log.error("list,e:{}", e);
}
return BaseResponse.error(CommonErrorCode.FAILED, "获取列表失败", null);
}
public BaseResponse<Map<String, List<Object>>> list(String bucket) {
try {
minioUtil.makeBucket(bucket);
Iterable<Result<Item>> myObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket).build());
Iterator<Result<Item>> iterator = myObjects.iterator();
List<Object> items = new ArrayList<>();
String format = "{'fileName':'%s','fileSize':'%s', 'uploadedTime':'%s', 'resignedObjectUrl':'%s'}";
while (iterator.hasNext()) {
Item item = iterator.next().get();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of("Asia/Shanghai"));
String resignedObjectUrl = minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs
.builder()
.method(Method.GET)
.bucket(bucket)
.object(item.objectName())
.expiry(minioProperty.getExpiry(), TimeUnit.HOURS)
.build());
String strTime = "";
if (!item.isDir()) {
strTime = item.lastModified().format(formatter);
}
items.add(JSON.parse(String.format(
format,
item.objectName(),
minioUtil.formatFileSize(item.size()),
strTime,
resignedObjectUrl)));
}
Map<String, List<Object>> fileList = new HashMap<>();
fileList.put(MinioService.KEY_FILELIST, items);
return BaseResponse.success(fileList);
} catch (Exception e) {
log.error("list,e:{}", e);
}
return BaseResponse.error(CommonErrorCode.FAILED, "获取列表失败", null);
}
public BaseResponse<String> downloadObject(String bucket, String objectName, String fileName) {
try {
log.info("objectName:{},fileName:{}",objectName, fileName);
minioClient.downloadObject(DownloadObjectArgs.builder().bucket(bucket).object(objectName).filename(fileName).build());
return BaseResponse.success(fileName);
} catch (Exception e) {
log.error("downloadObject,e:{}", e);
return BaseResponse.FAILED();
}
}
private void getList(String format, List<Object> items, String bucketName, String minioPath) throws Exception {
Iterable<Result<Item>> myObjects;
if (minioPath.equals("")) {
myObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
} else {
myObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(minioPath).build());
}
Iterator<Result<Item>> iterator = myObjects.iterator();
while (iterator.hasNext()) {
Item item = iterator.next().get();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of("Asia/Shanghai"));
String resignedObjectUrl = minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs
.builder()
.method(Method.GET)
.bucket(bucketName)
.object(item.objectName())
.expiry(minioProperty.getExpiry(), TimeUnit.HOURS)
.build());
if (!item.isDir()) {
items.add(JSON.parse(String.format(
format,
item.objectName(),
minioUtil.formatFileSize(item.size()),
item.lastModified() != null ? item.lastModified().format(formatter) : "",
resignedObjectUrl)));
} else {
getList(format, items, bucketName, minioPath + File.separator + item.objectName());
}
}
}
}
MinioUtil.java
package com.ym.minio.service.impl;
import com.ym.minio.base.config.MinioProperty;
import com.ym.minio.base.constant.MinioBucketHandle;
import com.ym.minio.service.MinioService;
import io.minio.*;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class MinioUtil {
@Autowired
private MinioClient minioClient;
@Autowired
private MinioProperty minioProperty;
/**
* @param bucketName 存储桶名称
*/
public int makeBucket(String bucketName) {
try {
//判断存储桶存不存在
BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(bucketName).build();
boolean bucketExisted = minioClient.bucketExists(bucketExistsArgs);
//创建桶
if (!bucketExisted) {
MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(bucketName).build();
minioClient.makeBucket(makeBucketArgs);
log.debug("bucketName:{},created", bucketName);
return MinioBucketHandle.BUCKETCREATED_SUCCESS.getState();
} else {
log.debug("bucketName:{},existed", bucketName);
return MinioBucketHandle.BUCKETCREATED_FAILURE.getState();
}
} catch (Exception e) {
log.error("makeBucket,e:{}", e);
return MinioBucketHandle.BUCKETCREATED_EXCEPTION.getState();
}
}
/**
*
* @param bucketName 存储桶名称
* @throws Exception
*/
public int removeBucket(String bucketName) {
try {
BucketExistsArgs exist = BucketExistsArgs.builder().bucket(bucketName).build();
boolean result = minioClient.bucketExists(exist);
if (!result) {
return MinioBucketHandle.BUCKETDEL_FAILURE.getState();
}
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
log.debug("removeBucket success");
return MinioBucketHandle.BUCKETDEL_SUCCESS.getState();
} catch (Exception e) {
log.error("removeBucket,e:{}", e);
return MinioBucketHandle.BUCKETDEL_EXCEPTION.getState();
}
}
/**
* @param bucketName 存储桶名称。
* @param objectName 存储桶里的对象名称
* @return
* @throws Exception
*/
public StatObjectResponse statObject(String bucketName,String objectName) {
try {
StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
return statObjectResponse;
} catch (Exception e) {
log.error("statObject, e:{}", e);
return null;
}
}
/**
* 上传文件
* @param files 文件
* @param bucketName 存储桶名称
*/
public List<Map<String, Object>> putObject(String dir, MultipartFile[] files, String bucketName) {
String path = "";
if (!StringUtils.isEmpty(dir)) {
path = dir;
}
List<Map<String, Object>> ret = new ArrayList<>();
for (MultipartFile multipartFile : files) {
//获取文件名
String orgfileName = multipartFile.getOriginalFilename();
Map<String, Object> m = new HashMap<>();
m.put(MinioService.KEY_FILENAME, orgfileName);
try {
//文件上传
InputStream in = multipartFile.getInputStream();
minioClient.putObject(
PutObjectArgs.
builder().
bucket(bucketName).
object(path + orgfileName)
.stream(in, multipartFile.getSize(), -1)
.contentType(multipartFile.getContentType())
.build());
String presignedObjectUrl = minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs
.builder()
.method(Method.GET)
.bucket(bucketName)
.object(path + orgfileName)
.expiry(minioProperty.getExpiry(), TimeUnit.HOURS)
.build());
m.put(MinioService.KEY_PRESIGNEDOBJECTURL, presignedObjectUrl);
ret.add(m);
in.close();
} catch (Exception e) {
log.error("putObject,e:{}", e);
}
}
return ret;
}
/**
* @param bucketName 存储桶名称
* @param objectName 文件名称
* @throws Exception
*/
public void removeObject(String bucketName, String objectName) {
try {
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
} catch (Exception e) {
log.error("removeObject,e:{}", e);
}
}
/**
* 生成一个给HTTP GET请求用的presigned URL。
* 浏览器/移动端的客户端可以用这个URL进行下载,
* 即使其所在的存储桶是私有的。
* 这个presigned URL可以设置一个失效时间,默认值是7天
*
* @param bucketName 存储桶名称
* @param objectName 文件名称
* @param expiry 失效时间(单位小时)
* @return
*/
public String getPresignedObjectUrl(String bucketName, String objectName, Integer expiry) {
log.info("bucketName = {}",bucketName);
log.info("objectName = {}",objectName);
log.info("expiry = {}",expiry);
try {
String presignedObjectUrl = minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs
.builder()
.method(Method.GET)
.bucket(bucketName)
.object(objectName)
.expiry(expiry, TimeUnit.HOURS)
.build());
log.info("presignedObjectUrl:{}", presignedObjectUrl);
return presignedObjectUrl;
} catch (Exception e) {
log.error("getPresignedObjectUrl,e:{}", e);
}
return null;
}
public static String formatFileSize(long fileS) {
DecimalFormat df = new DecimalFormat("#.00");
String fileSizeString = "";
if (fileS == 0) {
return MinioService.FILESIZE_0B;
}
if (fileS < 1024) {
fileSizeString = df.format((double) fileS) + " B";
} else if (fileS < 1048576) {
fileSizeString = df.format((double) fileS / 1024) + " KB";
} else if (fileS < 1073741824) {
fileSizeString = df.format((double) fileS / 1048576) + " MB";
} else {
fileSizeString = df.format((double) fileS / 1073741824) + " GB";
}
return fileSizeString;
}
}