华为云云耀云服务器L实例评测|基于云服务器的minio部署手册
【软件安装版本】【集群安装(是)(否)】
版本 |
创建人 |
修改人 |
创建时间 |
备注 |
1.0 |
jz |
jz |
2023.9.2 |
minio华为云耀服务器 |
|
|
|
|
|
|
|
|
|
|
一. 部署规划与架构
1. 规划:(集群:网络规划,服务器规划)
安装方式 :非集群安装
服务器 : 华为云:192.168.0.147
端口号 :A.minio管理界面:9000,33806 B.demo应用:8081
2. 架构(集群:拓扑图)

3. 支撑业务
小文件服务器:图片,文件,视频的存储管理。
二. 运行环境安装
- 硬件
华为云耀服务器,2核2G 3m网络
2. 操作系统

为了springboot的demo运行,需要提前安装好Java运行环境。这里不再重述
3. 环境配置
软件安装路径:/opt/software/minio
mkdir -p /opt/software/minio |

数据文件路径:/opt/data/minio

日志文件路径: /opt/data/minio/logs
mkdir -p /opt/data/minio/logs |

三. 单机部署步骤
- 安装包获取与安装
下载地址;https://min.io/download#/linux
cd /opt/software/minio/
wget https://dl.min.io/server/minio/release/linux-amd64/minio |

安装成功:

查看下载文件:

2. 配置修改
# 启动的时候看提示 新版本
MINIO_ROOT_USER=username
MINIO_ROOT_PASSWORD=password
# 如果MinIO版本比较旧,修改用户名密码为
MINIO_ACCESS_KEY=username
MINIO_SECRET_KEY=password |

注意:创建的文件是个新文件。
修改环境变量
增加两行
export MINIO_ROOT_USER=username
export MINIO_ROOT_PASSWORD=password
# 如果MinIO版本比较旧,修改用户名密码为
export MINIO_ACCESS_KEY=username
export MINIO_SECRET_KEY=password |

配置文件生效

3. 检测依赖环境是否就绪
4.安装
赋予文件安装权限:

以守护进程方式启动,指定端口为9000固定端口,数据文件路径/opt/data/minio/
nohup /opt/software/minio/minio server --console-address :33806 --address 0.0.0.0:9000 /opt/data/minio > /opt/data/minio/logs/minio.log & |

ps -ef |grep minio |
more minio.log |


Minio启动成功
5. 云服务器开通公网端口号:9000

登录华为云账号:https://auth.huaweicloud.com/



配置到服务器

进入控制台,更改安全组

选择刚才创建的安全组

- 验证
浏览器输入:公网ip:9000
会自动跳转到33806端口上。

用username/password登录

四.应用部署
1. 安装java
请自行安装jdk文件,保证java -version可用。

2.编写项目
Pom文件中增加配置
<!--MinIO JAVA SDK-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
</dependency>
|
Yaml中增加配置
minio:
endpoint: http://192.168.0.213:9000 #MinIO服务所在地址
bucketName: mall #存储桶名称
bucketImg: img # 图片桶
bucketVideo: video # 视频桶
accessKey: fileadmin #访问的key
secretKey: fileadmin #访问的秘钥
|
编写工具类
package top.fairy.global.globalfairytoppi4j.utils;
import io.minio.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import top.fairy.global.globalfairytoppi4j.beans.ResultEntity;
import top.fairy.global.globalfairytoppi4j.config.MinioClientConfig;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
@Slf4j
@Component
public class MinioUtil {
/**
* Minio文件上传
*
* @param file 文件实体
* @param fileName 修饰过的文件名 非源文件名
* @param bucketName 所存文件夹(桶名)
* @return
*/
public ResultEntity<Map<String, Object>> minioUpload(MultipartFile file, String fileName, String bucketName) {
ResultEntity<Map<String, Object>> resultEntity = new ResultEntity();
try {
MinioClient minioClient = MinioClientConfig.getMinioClient();
// fileName为空,说明要使用源文件名上传
if (fileName == null) {
fileName = file.getOriginalFilename();
fileName = fileName.replaceAll(" ", "_");
}
InputStream inputStream = file.getInputStream();
PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(fileName)
.stream(inputStream, file.getSize(), -1).contentType(file.getContentType()).build();
//文件名称相同会覆盖
minioClient.putObject(objectArgs);
return resultEntity;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 检查存储桶是否存在
*
* @param bucketName 存储桶名称
* @return
*/
public boolean bucketExists(String bucketName) {
boolean flag = false;
try {
flag = MinioClientConfig.bucketExists(bucketName);
if (flag) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return false;
}
/**
* 获取文件流
*
* @param fileName 文件名
* @param bucketName 桶名(文件夹)
* @return
*/
public InputStream getFileInputStream(String fileName, String bucketName) {
try {
MinioClient minioClient = MinioClientConfig.getMinioClient();
return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
}
return null;
}
/**
* @param bucketName:
* @author
* @description: 创建桶
* @date 2022/8/16 14:36
*/
public void createBucketName(String bucketName) {
try {
if (StringUtils.isBlank(bucketName)) {
return;
}
MinioClient minioClient = MinioClientConfig.getMinioClient();
boolean isExist = MinioClientConfig.bucketExists(bucketName);
if (isExist) {
log.info("Bucket {} already exists.", bucketName);
} else {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
}
}
/**
* 下载文件
*
* @param originalName 文件路径
*/
public InputStream downloadFile(String bucketName, String originalName, HttpServletResponse response) {
try {
MinioClient minioClient = MinioClientConfig.getMinioClient();
InputStream file = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(originalName).build());
String filename = new String(originalName.getBytes("ISO8859-1"), StandardCharsets.UTF_8);
if (StringUtils.isNotBlank(originalName)) {
filename = originalName;
}
response.setHeader("Content-Disposition", "attachment;filename=" + filename);
ServletOutputStream servletOutputStream = response.getOutputStream();
int len;
byte[] buffer = new byte[1024];
while ((len = file.read(buffer)) > 0) {
servletOutputStream.write(buffer, 0, len);
}
servletOutputStream.flush();
file.close();
servletOutputStream.close();
return file;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* @param bucketName:
* @description: 删除桶
* @date 2022/8/16 14:36
*/
public void deleteBucketName(String bucketName) {
try {
if (StringUtils.isBlank(bucketName)) {
return;
}
MinioClient minioClient = MinioClientConfig.getMinioClient();
boolean isExist = MinioClientConfig.bucketExists(bucketName);
if (isExist) {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
}
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
}
}
/**
* @param bucketName:
* @description: 删除桶下面所有文件
* @date 2022/8/16 14:36
*/
public void deleteBucketFile(String bucketName) {
try {
if (StringUtils.isBlank(bucketName)) {
return;
}
MinioClient minioClient = MinioClientConfig.getMinioClient();
boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (isExist) {
minioClient.deleteBucketEncryption(DeleteBucketEncryptionArgs.builder().bucket(bucketName).build());
}
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
}
}
/**
* 根据文件路径得到预览文件绝对地址
*
* @param bucketName
* @param fileName
* @return
*/
public String getPreviewFileUrl(String bucketName, String fileName) {
try {
MinioClient minioClient = MinioClientConfig.getMinioClient();
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(fileName).build());
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
}
|
编写配置文件
package top.fairy.global.globalfairytoppi4j.config;
import io.minio.BucketExistsArgs;
import io.minio.MinioClient;
import io.minio.messages.Bucket;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
@Slf4j
@Component
@Configuration
public class MinioClientConfig {
@Autowired
private StorageProperty storageProperty;
private static MinioClient minioClient;
/**
* @description: 获取minioClient
* @date 2021/6/22 16:55
* @return io.minio.MinioClient
*/
public static MinioClient getMinioClient(){
return minioClient;
}
/**
* 判断 bucket是否存在
*
* @param bucketName:
* 桶名
* @return: boolean
* @date : 2020/8/16 20:53
*/
@SneakyThrows(Exception.class)
public static boolean bucketExists(String bucketName) {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
}
/**
* 获取全部bucket
*
* @param :
* @return: java.util.List<io.minio.messages.Bucket>
* @date : 2020/8/16 23:28
*/
@SneakyThrows(Exception.class)
public static List<Bucket> getAllBuckets() {
return minioClient.listBuckets();
}
/**
* 初始化minio配置
*
* @param :
* @return: void
* @date : 2020/8/16 20:56
*/
@PostConstruct
public void init() {
try {
minioClient = MinioClient.builder()
.endpoint(storageProperty.getEndpoint())
.credentials(storageProperty.getAccessKey(), storageProperty.getSecretKey())
.build();
} catch (Exception e) {
e.printStackTrace();
log.error("初始化minio配置异常: 【{}】", e.fillInStackTrace());
}
}
}
|
编写业务类
package top.fairy.global.globalfairytoppi4j.action;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import top.fairy.global.globalfairytoppi4j.api.CommonResult;
import top.fairy.global.globalfairytoppi4j.beans.BucketPolicyConfigDto;
import top.fairy.global.globalfairytoppi4j.beans.FileVo;
import top.fairy.global.globalfairytoppi4j.beans.MinioUploadDto;
import top.fairy.global.globalfairytoppi4j.beans.PageUtil;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
/**
* MinIO对象存储管理
* Created by macro on 2019/12/25.
*/
//@Api(tags = "MinioController", description = "MinIO对象存储管理")
@Controller
@RequestMapping("/minio")
public class MinioController {
private static final Logger LOGGER = LoggerFactory.getLogger(MinioController.class);
@Value("${minio.endpoint}")
private String ENDPOINT;
@Value("${minio.bucketName}")
private String BUCKET_NAME;
@Value("${minio.bucketImg}")
private String BUCKET_IMG;
@Value("${minio.bucketVideo}")
private String BUCKET_VIDEO;
@Value("${minio.accessKey}")
private String ACCESS_KEY;
@Value("${minio.secretKey}")
private String SECRET_KEY;
// @ApiOperation("文件上传")
@RequestMapping(value = "/upload", method = RequestMethod.POST)
@ResponseBody
public CommonResult upload(@RequestPart("file") MultipartFile file) {
try {
//创建一个MinIO的Java客户端
MinioClient minioClient =MinioClient.builder()
.endpoint(ENDPOINT)
.credentials(ACCESS_KEY,SECRET_KEY)
.build();
boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(BUCKET_NAME).build());
if (isExist) {
LOGGER.info("存储桶已经存在!");
} else {
//创建存储桶并设置只读权限
minioClient.makeBucket(MakeBucketArgs.builder().bucket(BUCKET_NAME).build());
BucketPolicyConfigDto bucketPolicyConfigDto = createBucketPolicyConfigDto(BUCKET_NAME);
SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs.builder()
.bucket(BUCKET_NAME)
.config(JSONUtil.toJsonStr(bucketPolicyConfigDto))
.build();
minioClient.setBucketPolicy(setBucketPolicyArgs);
}
String filename = file.getOriginalFilename();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
// 设置存储对象名称
String objectName = sdf.format(new Date()) + "/" + filename;
// 使用putObject上传一个文件到存储桶中
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket(BUCKET_NAME)
.object(objectName)
.contentType(file.getContentType())
.stream(file.getInputStream(), file.getSize(), ObjectWriteArgs.MIN_MULTIPART_SIZE).build();
minioClient.putObject(putObjectArgs);
LOGGER.info("文件上传成功!");
MinioUploadDto minioUploadDto = new MinioUploadDto();
minioUploadDto.setName(filename);
minioUploadDto.setUrl(ENDPOINT + "/" + BUCKET_NAME + "/" + objectName);
return CommonResult.success(minioUploadDto);
} catch (Exception e) {
e.printStackTrace();
LOGGER.info("上传发生错误: {}!", e.getMessage());
}
return CommonResult.failed();
}
private BucketPolicyConfigDto createBucketPolicyConfigDto(String bucketName) {
BucketPolicyConfigDto.Statement statement = BucketPolicyConfigDto.Statement.builder()
.Effect("Allow")
.Principal("*")
.Action("s3:GetObject")
.Resource("arn:aws:s3:::"+bucketName+"/*.**").build();
return BucketPolicyConfigDto.builder()
.Version("2012-10-17")
.Statement(CollUtil.toList(statement))
.build();
}
/**
* 获取文件列表
*
* @param pageNum 页码
* @param pageSize 一页的数量
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value = "/fileList", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
public String getFileList(Integer pageNum, Integer pageSize) throws Exception {
String bucketName = "img";
//创建一个MinIO的Java客户端
MinioClient minioClient =MinioClient.builder()
.endpoint(ENDPOINT)
.credentials(ACCESS_KEY,SECRET_KEY)
.build();
DecimalFormat df = new DecimalFormat("0.00");
List<Bucket> buckets = minioClient.listBuckets();
List<FileVo> list = new ArrayList<>(32);
if (!buckets.isEmpty()) {
buckets.forEach(s -> {
try {
// 得到bucket下的文件
Iterable<Result<Item>> results = minioClient.listObjects(s.name());
// 循环遍历获取每一个文件对象
results.forEach(g -> {
try {
FileVo fileVo = new FileVo();
fileVo.setBucketName(s.name()); // 文件夹名称
fileVo.setFileName(g.get().objectName()); // 文件名称
fileVo.setUpdateTime(localDateTime2Date(g.get().lastModified().toLocalDateTime())); // 文件上传时间
Long size = g.get().size();
if (size > (1024 * 1024)) {
fileVo.setFileSize(df.format(((double) size / 1024 / 1024)) + "MB"); // 文件大小,如果超过1M,则把单位换成MB
} else if (size > 1024) {
fileVo.setFileSize(df.format(((double) size / 1024)) + "KB"); // 文件大小,如果没超过1M但是超过1000字节,则把单位换成KB
} else {
fileVo.setFileSize( size + "bytes"); // // 文件大小,如果没超过1000字节,则把单位换成bytes
}
list.add(fileVo);
} catch (ErrorResponseException e) {
e.printStackTrace();
} catch (InsufficientDataException e) {
e.printStackTrace();
} catch (InternalException e) {
e.printStackTrace();
} catch (InvalidBucketNameException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (XmlParserException e) {
e.printStackTrace();
} catch (ServerException e) {
e.printStackTrace();
}
});
} catch (XmlParserException e) {
e.printStackTrace();
}
});
}
JSONObject res = new JSONObject();
res.put("code", 200);
res.put("message", "获取文件列表成功");
// 按最后上传时间排序
list.sort(new Comparator<FileVo>() {
@Override
public int compare(FileVo o1, FileVo o2) {
return o2.getUpdateTime().compareTo(o1.getUpdateTime());
}
});
// 分页
List returnList = PageUtil.startPage(list, pageNum, pageSize);
res.put("list", returnList);
ObjectMapper mapper = new ObjectMapper();
String s = mapper.writeValueAsString(res);
return s;
}
private Date localDateTime2Date(LocalDateTime toLocalDateTime) {
Date date = Date.from( toLocalDateTime.atZone( ZoneId.systemDefault()).toInstant());
return date;
}
// @ApiOperation("文件删除")文件删除
@RequestMapping(value = "/delete", method = RequestMethod.POST)
@ResponseBody
public CommonResult delete(@RequestParam("objectName") String objectName) {
try {
MinioClient minioClient = MinioClient.builder()
.endpoint(ENDPOINT)
.credentials(ACCESS_KEY,SECRET_KEY)
.build();
minioClient.removeObject(RemoveObjectArgs.builder().bucket(BUCKET_NAME).object(objectName).build());
return CommonResult.success(null);
} catch (Exception e) {
e.printStackTrace();
}
return CommonResult.failed();
}
}
|
业务类service,dao入库等操作请自行补充。
源码下载地址:
global-fairy-top-pi4j: java语言读取dth11温湿度传感器参数,通过接口对外提供
具体版本参考:v0.3.1
- 打包部署
Idea中maven package项目之后,拷贝jar包到云服务器

- 启动
nohup java -jar global-fairy-top-pi4j-0.0.1-SNAPSHOT.jar &
- 测试:
localhost:8082/minio/fileList

- 常见问题
部署demo前请先安装mysql,并配置好地址。
所有评论(0)