文章目录
Minio图片服务器搭建
一、Minio是什么?
关于Minio,官网是这么介绍的:MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。
二、Linux环境下安装Minio server端
2.1.下载Minio server端
- 首先进入根目录下:
cd /
,下载包
wget https://dl.min.io/server/minio/release/linux-amd64/minio
2. 根目录下执行命令,创建文件夹然后复制文件到自己的目录下:
mkdir /user/minio
cp minio /usr/minio/
- 授权
chmod +x /usr/minio/minio
- 启动
MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin /usr/minio/minio server /usr/minio/data
后台启动
nohup /usr/minio/minio server /usr/minio/data > /usr/minio/minio.log 2>&1&
tips: /usr/minio/data 为你存储静态文件的目录, /usr/minio/data/minio.log为日志文件,可根据自己情况修改。
启动这里有很多坑,很多文章只有启动方法没有错误原因,官网也没做太多解释。我列举一下常见控制台输出:
第一个:Detected default credentials ‘minioadmin:minioadmin’, please change the credentials immediately using ‘MINIO_ACCESS_KEY’ and ‘MINIO_SECRET_KEY’。
这种方式是默认用户名、密码启动,提示让你改用户名和密码
第二种:Invalid credentials。常见于第一种启动方式,我测试时只要是没按默认用户名密码输入的,都有这种提示,但是不影响使用, 可以看到控制台输出了我们自定义的用户名密码,可以用此登录。
第三种:ERROR Unable to initialize the server: Unable to initialize sub-systems: Credentials missing。
后台启动时遇到这种错误,这是由于没有设置环境变量,设置一下就可以了
设置环境变量
export MINIO_ACCESS_KEY=你的用户名
export MINIO_SECRET_KEY=你的密码
然后执行后台启动命令nohup /usr/minio/minio server /usr/minio/data > /usr/minio/minio.log 2>&1 &
启动成功。
这里提一下,服务器一定要记得开放端口,自己的服务器关闭防火墙(不建议)或者开放一下端口
systemctl stop firewalld
或者开启9000端口
firewall-cmd --zone=public --add-port=9000/tcp --permanent
systemctl restart firewalld
2.2 外部测试
访问页面,输入你设置的用户名密码来登录:
点击右下角的加号,可以上传文件和创建桶,必须先创建一个桶才能上传文件。
我创建了一个test的桶,然后上传了一张图片
看一下服务器,已经上传成功了
编辑一下桶权限,设置为不限期查看,就可以分享了
访问方式:
http://ip:9000/test/5.jpg
三、项目下使用步骤
1.引入maven文件
代码如下:
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.1.4</version>
</dependency>
代码使用了hutool工具包,工具很全,推荐使用:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
2.使用Minio上传文件
Controller代码如下(示例):
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import io.minio.MinioClient;
import io.minio.policy.PolicyType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.text.SimpleDateFormat;
import java.util.Date;
@RestController
@RequestMapping("/minio")
public class FileController{
private static final Logger LOGGER = LoggerFactory.getLogger(FileController.class);
@Value("${minio.endpoint}")
private String ENDPOINT;
@Value("${minio.bucketName}")
private String BUCKET_NAME;
@Value("${minio.accessKey}")
private String ACCESS_KEY;
@Value("${minio.secretKey}")
private String SECRET_KEY;
@RequestMapping(value = "/upload", method = RequestMethod.POST)
public R upload(@RequestParam("file") MultipartFile file,String BUCKET_NAME) {
if (StrUtil.isBlank(BUCKET_NAME)) {
return doUpload(file);
} else {
return doUpload(file,BUCKET_NAME);
}
}
/**
* 文件上传
*/
public R doUpload(@RequestParam("file") MultipartFile file) {
try {
FileResult fileResult = new FileResult();
MinioClient minioClient = new MinioClient(ENDPOINT, ACCESS_KEY, SECRET_KEY);
boolean isExist = minioClient.bucketExists(BUCKET_NAME);
if (isExist) {
LOGGER.info("存储桶已经存在!");
} else {
//创建存储桶并设置只读权限
minioClient.makeBucket(BUCKET_NAME);
minioClient.setBucketPolicy(BUCKET_NAME, "*.*", PolicyType.READ_ONLY);
}
String originalFilename = file.getOriginalFilename();
String filename = IdUtil.simpleUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
// 设置存储对象名称
String objectName = sdf.format(new Date()) + "/" + filename;
// 使用putObject上传一个文件到存储桶中
minioClient.putObject(BUCKET_NAME, objectName, file.getInputStream(), file.getContentType());
LOGGER.info("文件上传成功!");
fileResult.setFilePath(ENDPOINT + "/" + BUCKET_NAME + "/" + objectName);
fileResult.setOriginName(originalFilename);
fileResult.setFileName(filename);
fileResult.setFileSize(file.getSize());
return R.ok(fileResult);
} catch (Exception e) {
LOGGER.info("上传发生错误: {}!", e.getMessage());
return R.fail("上传发生错误");
}
}
/**
* 文件删除
*/
@RequestMapping(value = "/delete", method = RequestMethod.POST)
public R delete(@RequestParam("objectName") String objectName) {
try {
MinioClient minioClient = new MinioClient(ENDPOINT, ACCESS_KEY, SECRET_KEY);
minioClient.removeObject(BUCKET_NAME, objectName);
return R.ok();
} catch (Exception e) {
e.printStackTrace();
}
return R.fail("删除失败");
}
/**
* 自定义桶位置
* @param file
* @param BUCKET_NAME
* @return
*/
public R doUpload( MultipartFile file,String BUCKET_NAME) {
try {
FileResult fileResult = new FileResult();
MinioClient minioClient = new MinioClient(ENDPOINT, ACCESS_KEY, SECRET_KEY);
boolean isExist = minioClient.bucketExists(BUCKET_NAME);
if (isExist) {
LOGGER.info("存储桶已经存在!");
} else {
//创建存储桶并设置只读权限
minioClient.makeBucket(BUCKET_NAME);
minioClient.setBucketPolicy(BUCKET_NAME, "*.*", PolicyType.READ_ONLY);
}
String originalFilename = file.getOriginalFilename();
String filename = IdUtil.simpleUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
// 设置存储对象名称
String objectName = filename;
// 使用putObject上传一个文件到存储桶中
minioClient.putObject(BUCKET_NAME, objectName, file.getInputStream(), file.getContentType());
LOGGER.info("文件上传成功!");
fileResult.setFilePath(ENDPOINT + "/" + BUCKET_NAME + "/" + objectName);
fileResult.setOriginName(originalFilename);
fileResult.setFileName(filename);
fileResult.setFileSize(file.getSize());
return R.ok(fileResult);
} catch (Exception e) {
LOGGER.info("上传发生错误: {}!", e.getMessage());
return R.fail("上传发生错误");
}
}
}
返回值R可根据自己需要自行封装,我的如下:
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import ****************************************.Constants;
import java.io.Serializable;
/**
* 返回前端 数据封闭类
*/
public class R implements Serializable {
private static final long serialVersionUID = 1L;
private Integer code;
private String msg;
private Object data;
private Long total; // 分页信息:总条数
public R() { }
private R(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
if (data instanceof Page<?>) {
Page<?> page = (Page<?>) data;
this.total = page.getTotal();
this.data = page.getRecords();
} else {
this.data = data;
}
}
public static R ok() {
return new R(Constants.OK_CODE, Constants.OK_MSG, null);
}
public static R ok(Object data) {
return new R(Constants.OK_CODE, Constants.OK_MSG, data);
}
public static R ok(String msg, Object data) {
return new R(Constants.OK_CODE, msg, data);
}
public static R fail(String msg) {
return new R(Constants.FAIL_CODE, msg, null);
}
public static R invalidToken(String msg) {
return new R(Constants.INVALID_TOKEN_CODE, msg, null);
}
public static R fail(int errorCode, String msg) {
return new R(errorCode, msg, null);
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
public Object getData() {
return data;
}
public Long getTotal() {
return total;
}
public R setTotal(Long total) {
this.total = total;
return this;
}
}
Constants内容:
/**
* 常量类
*/
public class Constants {
public final static int OK_CODE = 200;
public final static int FAIL_CODE = 400;
public final static int OTHER_FAIL_CODE = 333; // 其它错误
public final static int INVALID_TOKEN_CODE = 10000; // 其它错误
public final static String OK_MSG = "请求成功";
public final static String FAIL_MSG = "请求失败";
public final static int STATUS_0 = 0; // 可用状态
public final static int STATUS_1 = 1; // 禁用状态
}
配置文件
minio:
endpoint: http://ip:9000 #MinIO服务所在地址
bucketName: photos #存储桶名称
accessKey: minioadmin #访问的key
secretKey: minioadmin #访问的秘钥
uploadPath: /usr/minio/data #本地文件存放位置
总结
大体记录一下Minio的使用过程,windows的使用比较简单,有时间会补充上,欢迎批评指正。
2022年03月30日 针对8.0及以上版本更新
- 启动方式改变:
针对
- 【二、Linux环境下安装Minio server端】
- 【2.1 下载Minio server端 】
- 【2. 根目录下执行命令,创建文件夹然后复制文件到自己的目录下:】
- 【4.启动】
nohup /usr/minio/minio server /usr/minio/data > /usr/minio/minio.log 2>&1&
更改为:
nohup /usr/minio/minio server --address ':9000' --console-address ':9001' /usr/minio/data > /usr/minio/minio.log 2>&1&
需要指定api端口和web端口。
- 【4.启动】
- 【2. 根目录下执行命令,创建文件夹然后复制文件到自己的目录下:】
- 【2.1 下载Minio server端 】
- 代码更改
- 初始化客户端
MinioClient minioClient = new MinioClient(LOCALENDPOINT, ACCESS_KEY, SECRET_KEY);
更改为
MinioClient minioClient = MinioClient.builder().endpoint(LOCALENDPOINT)
.credentials(ACCESS_KEY, SECRET_KEY)
.build();
- 判断存储桶是否存在
boolean isExist = minioClient.bucketExists(bucketName);
变为
boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
- 创建存储桶并设置只读权限。
minioClient.makeBucket(bucketName);
变为
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
只读权限,这个更改十分蛋疼,更可气的是他标准改了官方文档还不更新,建议直接登录web端设置。
minioClient.setBucketPolicy(bucketName, "*.*", PolicyType.READ_ONLY);
变为一个json字符串的config,搜索了个config 的json串:
{
"Statement": [
{
"Action": [
"s3:GetBucketLocation",
"s3:ListBucket"
],
"Effect": "Allow",
"Principal": "*",
"Resource": "arn:aws:s3:::my-bucketname"
},
{
"Action": "s3:GetObject",
"Effect": "Allow",
"Principal": "*",
"Resource": "arn:aws:s3:::my-bucketname/myobject*"
}
],
"Version": "2012-10-17"
}
- 使用putObject上传一个文件到存储桶中
minioClient.putObject(bucketName, objectName + postfix, inputStream, file.getContentType());
变为
PutObjectArgs objectArgs = PutObjectArgs.builder().object(objectName + postfix)
.bucket(bucketName)
.contentType("application/octet-stream")
.stream(inputStream, inputStream.available(), -1).build();
minioClient.putObject(objectArgs);
其他一些删除、获取文件流、设置访问权限等的不想写了,就是这么任性。