什么是Minio
Minio是GlusterFS创始人之一Anand Babu Periasamy发布新的开源项目,基于Apache License v2.0开源协议的对象存储项目,采用Golang实现,其客户端支持现主流的开发语言,如Java、javascript、Python以及Golang
安装Minio
Minio可以在K8S、Docker等容器中安装,也可以直接进行安装,或者是通过编译源码进行安装(该方式官方不推荐使用)
在容器中安装的方式可以参考官网的安装方式,这里不做介绍
- Docker:min.io/download#/d…
- K8S:min.io/download#/k…
这里要说的是在Linux(Ubuntu)中进行安装,参考地址:min.io/download#/l…
需注意的是,系统环境除了Windows之外,安装方式除了源码编译以及K8S安装,这三种情况下,其他的安装都需要确定系统的位数及架构(ARM或者是其他的),这里默认是 ARM 架构
第一步 安装
确定自己需要安装的路径,这里的安装路径 /usr/development/minio
,系统自身并不存在目录,因此需要创建
$ cd /usr
$ mkdir development
$ cd development
$ mkdir minio
接下来根据官网所安装步骤安装即可,这里采用官网的 Binary 进行安装
$ wget https://dl.min.io/server/minio/release/linux-amd64/minio
# 修改minio文件的读写权限
$ chmod +x minio
# 启动
$ MINIO_ROOT_USER=admin MINIO_ROOT_PASSWORD=password ./minio server /mnt/data --console-address ":9001"
当启动的时候,会惊奇的发现出现以下的报错信息,不要慌,按照报错的提示进行修改即可
在原启动命令的基础上,再加上 --address ":9000"
即可
出现这个报错主要是因为Minio需要两个启动端口,不指定的话,会随机使用两个端口作为启动端口的
- --console-address:主要是minio的web管理界面的端口,可视化管理的
- --address:主要是做了客户端访问的端口
相关命令解释
- MINIO_ROOT_USER: Minio web管理界面的登录用户名
- MINIO_ROOT_PASSWORD: Minio web管理界面的登录密码
- ./minio: 当前目录下的minio文件
- server: 开启服务
- /mnt/data: 数据存储的目录
当看到以下信息说明已经启动完成了
这里可以看到,我这个报了 WARNING: Console endpoint is listening on a dynamic port (45979), please use --console-address ":PORT" to choose a static port.
的错误
这是因为我这里只指定了客户端访问的端口,没有指定web管理界面的端口
需注意的是,虽然启动成功了,但是这种情况下,只要关闭终端窗口,Minio也会关闭掉
我们希望Minio后台运行,因此需要使用到 nohup
命令
这里创建了一个启动脚本,方便运行 vim start.sh
nohup /usr/development/minio/minio server /usr/development/minio/data/ --address ":9000" --console-address ":9001" > /usr/development/minio/minio.log 2>&1 &
:wq
保存退出
在ubuntu使用的是 bash
命令运行sh
脚本,而在centos下则使用 sh
运行
再度尝试访问Minio的web管理界面,发现需要输入账号跟密码,但启动脚本中并没有设置登录用户名和密码
官方早已想到了这一点,因此提供了一个默认的账号和密码:minioadmin:minioadmin
实际使用的过程中,并不会去使用默认的账号和密码
使用脚本命令启动服务的方式,可以通过设置环境变量实现设置默认的账号和密码
``
vim ~/.profile
export MINIO_ROOT_USER=test
export MINIO_ROOT_PASSWORD=test@123
这里需要注意的是,密码的长度最低是8位
:wq
保存退出,刷新一下环境变量 source ~/.profile
,然后系统重启一下,再启动Minio
第二步 使用
首先需要创建一个 Bucket
,翻译过来就是 桶
输入 Bucket Name后点击 Create Bucket
即可,其他的参数请自行了解
这样就得到了一个存储的桶
可点击右上角的 Upload
直接上传文件,这里不多做介绍
SpringBoot集成
创建一个SpringBoot项目
项目目录
pom.xml 依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.4.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
application.yml 配置文件
server:
port: 8760
spring:
application:
name: minio-demo
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
minio:
endpoint: http://192.168.71.131:9000
accessKey: xiaomo
secretKey: xiaomo@123
bucketName: test-bucket
创建一个 Minio 配置文件 MinioProperties.java
@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
private String endpoint;
private String accessKey;
private String secretKey;
private String bucketName;
}
MinioConfig.java
@Configuration
public class MinioConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(MinioConfig.class);
@Resource
private MinioProperties properties;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(properties.getEndpoint())
.credentials(properties.getAccessKey(), properties.getSecretKey())
.build();
}
public void initBucket() {
LOGGER.info("bucket start create");
MinioClient minioClient = minioClient();
try {
boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(properties.getBucketName()).build());
if (!exists) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(properties.getBucketName()).build());
LOGGER.info("bucket create success, bucket name >>> {} <<<", properties.getBucketName());
return;
}
LOGGER.warn("bucket already exists, bucket name >>> {} <<<", properties.getBucketName());
} catch (Exception e) {
LOGGER.error("bucket create exception.", e);
}
}
}
创建 一个操作Minio的服务 MinioService.java
@Component
public class MinioService {
private static final Logger LOGGER = LoggerFactory.getLogger(MinioService.class);
@Resource
private MinioConfig minioConfig;
@Resource
private MinioProperties properties;
/** 根据File类进行上传 */
public boolean upload(File file) {
MinioClient minioClient = minioConfig.minioClient();
String filename = file.getName();
try {
minioClient.uploadObject(UploadObjectArgs.builder()
.bucket(properties.getBucketName())
.object(filename)
.filename(file.getAbsolutePath())
.build());
return true;
} catch (Exception e) {
LOGGER.error("file upload minio exception, file name: {}", filename, e);
return false;
}
}
/** 前端上传 */
public boolean upload(MultipartFile file) {
MinioClient minioClient = minioConfig.minioClient();
String filename = file.getOriginalFilename();
try {
InputStream inputStream = file.getInputStream();
minioClient.putObject(PutObjectArgs.builder()
.bucket(properties.getBucketName())
.object(filename)
.stream(inputStream, inputStream.available(), -1)
.contentType(file.getContentType())
.build());
return true;
} catch (Exception e) {
LOGGER.error("file upload minio exception, file name: {}", filename, e);
return false;
}
}
/** 上传到Minio指定路径 */
public boolean upload(String path, MultipartFile file) {
MinioClient minioClient = minioConfig.minioClient();
String filename = file.getOriginalFilename();
try {
filename = path + filename;
InputStream inputStream = file.getInputStream();
minioClient.putObject(PutObjectArgs.builder()
.bucket(properties.getBucketName())
.object(filename)
.stream(inputStream, inputStream.available(), -1)
.contentType(file.getContentType())
.build());
return true;
} catch (Exception e) {
LOGGER.error("file upload minio exception, file name: {}", filename, e);
return false;
}
}
/** 上传到Minio指定目录,可选择自定义随机文件名 */
public boolean upload(String path, MultipartFile file, Boolean isRandom) {
MinioClient minioClient = minioConfig.minioClient();
String filename = file.getOriginalFilename();
if (Boolean.TRUE.equals(isRandom)) {
long millis = System.currentTimeMillis();
String extension = FilenameUtils.getExtension(filename);
filename = millis + "." + extension;
}
try {
filename = path + filename;
InputStream inputStream = file.getInputStream();
minioClient.putObject(PutObjectArgs.builder()
.bucket(properties.getBucketName())
.object(filename)
.stream(inputStream, inputStream.available(), -1)
.contentType(file.getContentType())
.build());
return true;
} catch (Exception e) {
LOGGER.error("file upload minio exception, file name: {}, source file name: {}", filename, file.getOriginalFilename(), e);
return false;
}
}
/** response下载 */
public void download(String filename, HttpServletResponse response) {
MinioClient minioClient = minioConfig.minioClient();
response.reset();
try(InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
.bucket(properties.getBucketName())
.object(filename)
.build())) {
filename = filename.substring(filename.lastIndexOf("/") + 1);
ServletOutputStream outputStream = response.getOutputStream();
response.setContentType("application/octet-stream");
response.setCharacterEncoding("utf-8");
response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
byte[] bytes = new byte[1024];
int len;
while ((len = inputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, len);
}
outputStream.close();
} catch (Exception e) {
LOGGER.error("file download from minio exception, file name: {}", filename, e);
}
}
}
需要注意一点是,Minio在同一个路径下若出现两个文件名一样的文件的时候,会自动覆盖掉上一版的文件,只会留下最新的版本,因此封装了一个自定义文件名称的方法
新建 HomeController.java
@RestController
public class HomeController {
@Resource
private MinioService minioService;
@PostMapping("upload")
public String upload(MultipartFile file) {
boolean exists = minioService.upload("notes/",file, true);
return exists ? "上传成功" : "上传失败";
}
@GetMapping("download")
public void download(@RequestParam String filename, HttpServletResponse response) {
minioService.download(filename, response);
}
}
打开 PostMan,输入上传地址后,选择Body后将字段的类型选择为file,再从本地选择一张图片点击Send发送,响应成功
前往Minioweb管理端查看,点击 Refresh 出现一个目录,点击进行发现一个文件,说明已经成功上传了
文件下载
在 HomeController
已经写好了一个请求,请求方式是Get,所以直接打开浏览器,在地址栏输入请求地址,回车
这里为了方便,就直接在请求地址上输入文件在Minio全路径了
可以看到,成功的把图片下载下来了,
最后
若涉及到一些文件不想放到现有的对象存储服务上的话,可以使用Minio自行搭建一个文件管理系统
由于水平有限,若文章中有任何的错误,欢迎指正