一、单机minio搭建
1.下载rpm包
下载地址:https://dl.min.io/server/minio/release/linux-amd64/
?2.安装
rpm -ivh minio-20220504074527.0.0.x86_64.rpm
?修改 /etc/systemd/system/minio.service文件
#启动的用户和用户组
User=root
Group=root
#配置文件地址
EnvironmentFile=-/etc/default/minio.conf
?创建配置文件并编辑:
#创建文件存储路径
mkdir -p /opt/minio/data
#创建并编辑配置文件
touch /etc/default/minio.conf
vi /etc/default/minio.conf
#编辑内容,请确保MINIO_VOLUMES(文件存储)路径存在
MINIO_VOLUMES="/opt/minio/data"
MINIO_OPTS="--address :9001 --console-address :9000"
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
安装完成,启动,并查看状态:
#重新刷新
systemctl daemon-reload
#,启动
systemctl start minio
#查看状态
systemctl status minio
?3.浏览器访问:http://ip:9000
若无法访问则关闭防火墙或打开端口
?使用刚刚配置的账户信息登陆即可。
二、minio集群搭建
服务器 | 挂载磁盘路径 | 192.168.14.131 | /data/minio_data | 192.168.14.133 | /data/minio_data | 192.168.14.134 | /data/minio_data | 192.168.14.136 | /data/minio_data |
请在操作前关闭防火墙,保证服务器间端口通信正常,并保证时间同步
时间同步:
yum -y install ntp
ntpdate ntp1.alliyun.com
#开启自动同步
service ntpd start
#开机自启同步
systemctl enable ntpd
1.创建minio存储路径(4台机器均要执行)
mkdir -p /data/minio_data
2.挂载磁盘(4台机器均要执行)
?
?
?
?硬盘添加完成后输入:
fdisk -l
?如果没有看见底部对应磁盘,重启服务器后可见。
看到新磁盘/dev/sdb后,开始进行分区及格式化
#依次输入
fdisk /dev/sdb
m
n
p
1
#回车即可,出现下图后输入w
fdisk -l
?格式化成ext3的文件系统
mkfs -t ext3 /dev/sdb1
#自动挂载
vi /etc/fstab
#添加以下内容
/dev/sdb1 /data/minio_data ext3 defaults 0 1
#使刚才设置指令生效
mount -a
df -h
?注意:挂载后不要在/data/minio_data下放任何文件,挂载自动生成的lost+found可忽略。
2.创建文件(4台机器均要执行)
mkdir -p /data/minio/run/
3.下载minio文件(4台机器均要执行)
wget https://dl.min.io/server/minio/release/linux-amd64/minio
将minio文件移到/data/minio/run/路径下
mv minio /data/minio/run/minio
4.编辑启动脚本(4台机器均要执行)
vi /data/minio/run/run.sh
#以下为文件内容
#!/bin/bash
export MINIO_ROOT_USER=minioadmin
export MINIO_ROOT_PASSWORD=minioadmin
/data/minio/run/minio server --config-dir /etc/minio --address ":9000" --console-address ":9001" \
http://192.168.14.131/data/minio_data \
http://192.168.14.133/data/minio_data \
http://192.168.14.134/data/minio_data \
http://192.168.14.136/data/minio_data
注意里面的#!/bin/bash,不加的话后续使用服务启动会直接结束,会出现
main process exited, code=exited, status=203/EXEC
5.创建Minio.server,将minio加入系统服务(4台机器均要执行)
vim /usr/lib/systemd/system/minio.service
#以下为文件内容
[Unit]
Description=Minio service
Documentation=https://docs.minio.io/
[Service]
WorkingDirectory=/data/minio/run/
ExecStart=/data/minio/run/run.sh
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
赋予权限:
chmod +x /usr/lib/systemd/system/minio.service && chmod +x /data/minio/run/minio && chmod +x /data/minio/run/run.sh
6.启动集群:
注意:以下命令需以此在每台服务器执行,最少4块磁盘才能顺利启动,得保证4台机器都处于启动状态,并通信正常。(并非启动好一台再下一台)。
#重新刷新
systemctl daemon-reload
#启动,需在每台执行,需依照启动脚本里的服务器顺序执行
systemctl start minio.service
#4台服务器都执行启动命令后,过一段时间再查看状态,监听并连通需要几秒时间
systemctl status minio.service -l
若启动不成功,可采用/data/minio/run/run.sh直接启动,查看输出内容。
启动成功的界面如下图所示:
?浏览器访问即可看见:
三、Spring boot集成minio
pom.xml
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.0.3</version>
</dependency>
配置文件:
#指向API地址,非控制台地址
minio.url=http://192.168.14.135:9001
minio.accessKey=minioadmin
minio.secretKey=minioadmin
#文件桶名称
minio.bucketName=minuploadfiles
MinIoClientConfig.java
package MinioUtils;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class MinIoClientConfig {
@Value("${minio.url}")
private String url;
@Value("${minio.accessKey}")
private String accessKey;
@Value("${minio.secretKey}")
private String secretKey;
/**
* 注入minio 客户端
* @return
*/
@Bean
public MinioClient minioClient(){
return MinioClient.builder()
.endpoint(url)
.credentials(accessKey, secretKey)
.build();
}
}
ObjectItem.java
package MinioUtils;
public class ObjectItem {
private String objectName;
private Long size;
public String getObjectName() {
return objectName;
}
public void setObjectName(String objectName) {
this.objectName = objectName;
}
public Long getSize() {
return size;
}
public void setSize(Long size) {
this.size = size;
}
}
MinioHelp.java
package MinioUtils;
import baseUtils.MyException;
import baseUtils.ResultEnum;
import common.SystemConfig;
import io.minio.*;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class MinioHelp {
private final static Logger logger = LoggerFactory.getLogger(MinioHelp.class);
@Autowired
private MinioClient minioClient;
/**
* description: 判断bucket是否存在,不存在则创建
*
* @return: void
*/
public void existBucket(String name) {
try {
boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
if (!exists) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 创建存储bucket
* @param bucketName 存储bucket名称
* @return Boolean
*/
public Boolean makeBucket(String bucketName) {
try {
minioClient.makeBucket(MakeBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 删除存储bucket
* @param bucketName 存储bucket名称
* @return Boolean
*/
public Boolean removeBucket(String bucketName) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* description: 上传多个文件
*
* @param multipartFile
* @return: java.lang.String
*/
public List<String> upload(List<MultipartFile> multipartFile) {
List<String> names = new ArrayList<>(multipartFile.size());
String bucketName= SystemConfig.getProperty("minio.bucketName");
for (int i = 0; i < multipartFile.size(); i++) {
MultipartFile file=multipartFile.get(i);
String fileName = file.getOriginalFilename();
String[] split = fileName.split("\\.");
if (split.length > 1) {
fileName = split[0] + "_" + System.currentTimeMillis() + "." + split[1];
} else {
fileName = fileName + System.currentTimeMillis();
}
InputStream in = null;
try {
in = file.getInputStream();
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.stream(in, in.available(), -1)
.contentType(file.getContentType())
.build()
);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
names.add(fileName);
}
return names;
}
/**
* description: 上传单个文件
*/
public boolean uploadOne(String fileName,MultipartFile file) {
String bucketName= SystemConfig.getProperty("minio.bucketName");
InputStream in = null;
boolean isSucceed =false;
try {
in = file.getInputStream();
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.stream(in, in.available(), -1)
.contentType(file.getContentType())
.build()
);
isSucceed=true;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return isSucceed;
}
/**
* description: 下载文件
*
* @param fileName
* @return: org.springframework.http.ResponseEntity<byte [ ]>
*/
public ResponseEntity<byte[]> download(String fileName) {
ResponseEntity<byte[]> responseEntity = null;
InputStream in = null;
ByteArrayOutputStream out = null;
String bucketName= SystemConfig.getProperty("minio.bucketName");
try {
in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
out = new ByteArrayOutputStream();
IOUtils.copy(in, out);
//封装返回值
byte[] bytes = out.toByteArray();
HttpHeaders headers = new HttpHeaders();
try {
headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
headers.setContentLength(bytes.length);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setAccessControlExposeHeaders(Arrays.asList("*"));
responseEntity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return responseEntity;
}
/**
* 查看文件对象
* @param bucketName 存储bucket名称
* @return 存储bucket内文件对象信息
*/
public List<ObjectItem> listObjects(String bucketName) {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).build());
List<ObjectItem> objectItems = new ArrayList<>();
try {
for (Result<Item> result : results) {
Item item = result.get();
ObjectItem objectItem = new ObjectItem();
objectItem.setObjectName(item.objectName());
objectItem.setSize(item.size());
objectItems.add(objectItem);
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return objectItems;
}
/**
* 批量删除文件对象
* @param bucketName 存储bucket名称
* @param objects 对象名称集合
*/
public Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> objects) {
List<DeleteObject> dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());
Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
return results;
}
// 下载文件InputStream
public InputStream downloadInput(String fileName){
String bucketName= SystemConfig.getProperty("minio.bucketName");
try {
return minioClient.getObject(GetObjectArgs
.builder()
.bucket(bucketName)
.object(fileName)
.build());
} catch (Exception e) {
e.printStackTrace();
throw new MyException(ResultEnum.FAST_DFS_ERROR);
}
}
//保存文件到服务器
public String downFileToLocal(String fileName){
InputStream is =downloadInput(fileName);
String pathStr= SystemConfig.getProperty("SavePath");
String FolderStr = "imptemp";
String wholePath = pathStr + "uploadfiles" + (StringUtils.isBlank(FolderStr) ? "" : "/" + FolderStr+"/"+fileName);
File file = new File(wholePath);
logger.info("wholePath="+wholePath);
if(!file.exists()) {
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
BufferedInputStream in=null;
BufferedOutputStream out=null;
in=new BufferedInputStream(is);
out=new BufferedOutputStream(new FileOutputStream(wholePath));
int len=-1;
byte[] b=new byte[1024];
while((len=in.read(b))!=-1){
out.write(b,0,len);
}
in.close();
out.close();
return wholePath;
} catch (Exception e) {
e.printStackTrace();
throw new MyException(ResultEnum.FAST_DFS_ERROR);
}
}
}
使用示例:
@Autowired
private MinioHelp minioHelp;
//上传文件
@PostMapping("/MinioFileUpload.action")
public Object uploadFileInMinio(HttpServletRequest request){
logger.info("MinioFileUpload");
List<MultipartFile> files = ((MultipartHttpServletRequest) request).getFiles("file");
List<Map<String,String>> result=new ArrayList<Map<String,String>>();
for (int i = 0; i < files.size(); i++) {
MultipartFile file = files.get(i);
String newFileName =new Date().getTime()+ i+"." + extName;
try {
#上传文件
boolean isSucceed=minioHelp.uploadOne(newFileName,files.get(i));
if(!isSucceed){
throw new MyException(1037,"文件\""+fileName+"\"上传失败");
}
}catch (Exception e) {
e.printStackTrace();
throw new MyException(ResultEnum.FILE_UPLOAD_FAIL);
}
}
return ResultVOUtil.success(result);
}
//拉取文件到服务器
public void downFileToLocal(String fileName){
MinioHelp.downFileToLocal(oldFileName);
}
四、minio使用测试
创建文件桶:
?配置文件桶访问策略:
?
?可在控制台上传图片:
?通过浏览器访问:http://192.168.14.135:9001/文件桶名称/文件名称。
参考文档:
分布式文件服务minio集群搭建
Linux最新版minIO分布式集群部署+高可用
感谢各位大神在网络上分享
|