简介:
minio 是一个基于apache license v2.0开源协议的对象存储服务,虽然轻量,却拥有着不错的性能。它兼容亚马逊s3云存储服务接口,非常适合于存储大容量非结构化的数据。
例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几 kb 到最大 5t 不等。
最重要的是免费
说明:
docker如果想安装软件 , 必须先到docker镜像仓库下载镜像。
1、寻找minio镜像


2、下载minio镜像
# 下载镜像 docker pull minio/minio #查看镜像 docker images
| 命令 | 描述 |
|---|---|
| docker pull minio/minio | 下载最新版minio镜像 (其实此命令就等同于 : docker pull minio/minio:latest ) |
| docker pull minio/minio:release.2023-11-20t22-40-07z.fips | 下载指定版本的minio镜像 (xxx指具体版本号) |

3、创建目录
一个用来存放配置,一个用来存储上传文件的目录
启动前需要先创建minio外部挂载的配置文件( /opt/minio/config),和存储上传文件的目录( /opt/minio/data)
mkdir -p /opt/minio/config mkdir -p /opt/minio/data
4、创建minio容器并运行
docker run \ -p 19000:9000 \ -p 9090:9090 \ --net=host \ --name minio \ -d --restart=always \ -e "minio_access_key=minioadmin" \ -e "minio_secret_key=minioadmin" \ -v /opt/minio/data:/data \ -v /opt/minio/config:/root/.minio \ minio/minio server \ /data --console-address ":9090" -address ":19000"
| 命令 | 描述 |
|---|---|
| -p 9000:9000 -p 9090:9090 | 这是端口映射,前一个是服务器的端口,后一个是客户端也就是api接口访问的端口地址 |
| –name minio | 这是给新创建的容器命名的选项,名字是 “minio” |
| –net=host | 这是网络设置,表示容器将使用主机的网络栈,这样就不需要在容器内部配置网络 |
| -d --restart=always | 这是运行容器的其他选项,-d使容器在后台运行,–restart=always表示容器总是会在退出后自动重启 |
| -e “minio_access_key=minioadmin” | 用户名 |
| -e “minio_secret_key=minioadmin” | 密码 |
| -v /opt/minio/data:/data | 这意味着将宿主机上的 /opt/minio/data 目录挂载到容器内的 /data 目录 |
| -v /opt/minio/config:/root/.minio | 将宿主机上的 /opt/minio/config 目录挂载到容器内的 /root/.minio 目录 |
| minio/minio server /data --console-address “:9090” -address “:9000” | 这是容器内要运行的命令,启动一个名为 “minio” 的服务器,数据存储在 /data 目录下,服务器的控制台地址为 “:9090”,服务地址为 “:9000” |
| \ | 换行 |
4.1、访问操作
访问:http://47.117.160.102:9090/login 用户名:密码 minioadmin:minioadmin

4.2、创建用户


4.3、创建组


4.4、创建buckets


4.5、创建access keys

4.6、文件上传

4.7、浏览器访问上传文件

4.6.1、输入ip:19000/buckets名/文件名,如果不行,看看端口是否开放

5、springboot简单使用
5.1、添加minio依赖pom
<dependencies>
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-web</artifactid>
</dependency>
<!-- minio 客户端 -->
<dependency>
<groupid>io.minio</groupid>
<artifactid>minio</artifactid>
<version>8.5.7</version>
</dependency>
<dependency>
<groupid>org.projectlombok</groupid>
<artifactid>lombok</artifactid>
</dependency>
<dependency>
<groupid>org.apache.commons</groupid>
<artifactid>commons-lang3</artifactid>
<version>3.11</version>
</dependency>
</dependencies>
5.2、配置minio的yaml
server:
port: 3333
spring:
servlet:
multipart:
max-request-size: 200mb
max-file-size: 200mb
minio:
url: http://127.0.0.1:19000 #换成自己的minio服务端地址
accesskey: minioadmin # 用户名
secretkey: minioadmin # 密码
bucketname: demo # bucketname指的就是之前创建的minio桶bucket
5.3、配置类config
/**
* @author haoyang
* @create 2023-12-01 20:40
*/
@data
@configuration
public class minioconfig {
/**
* 访问地址
*/
@value("${minio.url}")
private string endpoint;
/**
* accesskey类似于用户id,用于唯一标识你的账户
*/
@value("${minio.accesskey}")
private string accesskey;
/**
* secretkey是你账户的密码
*/
@value("${minio.secretkey}")
private string secretkey;
/**
* 默认存储桶
*/
@value("${minio.bucketname}")
private string bucketname;
@bean
public minioclient minioclient() {
minioclient minioclient = minioclient.builder()
.endpoint(endpoint)
.credentials(accesskey, secretkey)
.build();
return minioclient;
}
}
5.4、创建minio工具类
/**
* minio工具类
*
*/
@slf4j
@component
@requiredargsconstructor
public class minioutils {
private final minioclient minioclient;
/****************************** operate bucket start ******************************/
/**
* 启动springboot容器的时候初始化bucket
* 如果没有bucket则创建
*
* @param bucketname
*/
@sneakythrows(exception.class)
private void createbucket(string bucketname) {
if (!bucketexists(bucketname)) {
minioclient.makebucket(makebucketargs.builder().bucket(bucketname).build());
}
}
/**
* 判断bucket是否存在,true:存在,false:不存在
*
* @param bucketname
* @return
*/
@sneakythrows(exception.class)
public boolean bucketexists(string bucketname) {
return minioclient.bucketexists(bucketexistsargs.builder().bucket(bucketname).build());
}
/**
* 获得bucket的策略
*
* @param bucketname
* @return
*/
@sneakythrows(exception.class)
public string getbucketpolicy(string bucketname) {
return minioclient.getbucketpolicy(getbucketpolicyargs
.builder()
.bucket(bucketname)
.build());
}
/**
* 获得所有bucket列表
*
* @return
*/
@sneakythrows(exception.class)
public list<bucket> getallbuckets() {
return minioclient.listbuckets();
}
/**
* 根据bucketname获取其相关信息
*
* @param bucketname
* @return
*/
@sneakythrows(exception.class)
public optional<bucket> getbucket(string bucketname) {
return getallbuckets().stream().filter(b -> b.name().equals(bucketname)).findfirst();
}
/**
* 根据bucketname删除bucket,true:删除成功; false:删除失败,文件或已不存在
*
* @param bucketname
* @throws exception
*/
@sneakythrows(exception.class)
public void removebucket(string bucketname) {
minioclient.removebucket(removebucketargs.builder().bucket(bucketname).build());
}
/****************************** operate bucket end ******************************/
/****************************** operate files start ******************************/
/**
* 判断文件是否存在
*
* @param bucketname
* @param objectname
* @return
*/
public boolean isobjectexist(string bucketname, string objectname) {
boolean exist = true;
try {
minioclient.statobject(statobjectargs.builder().bucket(bucketname).object(objectname).build());
} catch (exception e) {
log.error("[minio工具类]>>>> 判断文件是否存在, 异常:", e);
exist = false;
}
return exist;
}
/**
* 判断文件夹是否存在
*
* @param bucketname
* @param objectname
* @return
*/
public boolean isfolderexist(string bucketname, string objectname) {
boolean exist = false;
try {
iterable<result<item>> results = minioclient.listobjects(
listobjectsargs.builder().bucket(bucketname).prefix(objectname).recursive(false).build());
for (result<item> result : results) {
item item = result.get();
if (item.isdir() && objectname.equals(item.objectname())) {
exist = true;
}
}
} catch (exception e) {
log.error("[minio工具类]>>>> 判断文件夹是否存在,异常:", e);
exist = false;
}
return exist;
}
/**
* 根据文件前置查询文件
*
* @param bucketname 存储桶
* @param prefix 前缀
* @param recursive 是否使用递归查询
* @return minioitem 列表
*/
@sneakythrows(exception.class)
public list<item> getallobjectsbyprefix(string bucketname,
string prefix,
boolean recursive) {
list<item> list = new arraylist<>();
iterable<result<item>> objectsiterator = minioclient.listobjects(
listobjectsargs.builder().bucket(bucketname).prefix(prefix).recursive(recursive).build());
if (objectsiterator != null) {
for (result<item> o : objectsiterator) {
item item = o.get();
list.add(item);
}
}
return list;
}
/**
* 获取文件流
*
* @param bucketname 存储桶
* @param objectname 文件名
* @return 二进制流
*/
@sneakythrows(exception.class)
public inputstream getobject(string bucketname, string objectname) {
return minioclient.getobject(
getobjectargs.builder()
.bucket(bucketname)
.object(objectname)
.build());
}
/**
* 断点下载
*
* @param bucketname 存储桶
* @param objectname 文件名称
* @param offset 起始字节的位置
* @param length 要读取的长度
* @return 二进制流
*/
@sneakythrows(exception.class)
public inputstream getobject(string bucketname, string objectname, long offset, long length) {
return minioclient.getobject(
getobjectargs.builder()
.bucket(bucketname)
.object(objectname)
.offset(offset)
.length(length)
.build());
}
/**
* 获取路径下文件列表
*
* @param bucketname 存储桶
* @param prefix 文件名称
* @param recursive 是否递归查找,false:模拟文件夹结构查找
* @return 二进制流
*/
public iterable<result<item>> listobjects(string bucketname, string prefix, boolean recursive) {
return minioclient.listobjects(
listobjectsargs.builder()
.bucket(bucketname)
.prefix(prefix)
.recursive(recursive)
.build());
}
/**
* 使用multipartfile进行文件上传
*
* @param bucketname 存储桶
* @param file 文件名
* @param objectname 对象名
* @param contenttype 类型
* @return
*/
@sneakythrows(exception.class)
public objectwriteresponse uploadfile(string bucketname, multipartfile file, string objectname, string contenttype) {
inputstream inputstream = file.getinputstream();
return minioclient.putobject(
putobjectargs.builder()
.bucket(bucketname)
.object(objectname)
.contenttype(contenttype)
.stream(inputstream, inputstream.available(), -1)
.build());
}
/**
* 图片上传
* @param bucketname
* @param imagebase64
* @param imagename
* @return
*/
public objectwriteresponse uploadimage(string bucketname, string imagebase64, string imagename) {
if (!stringutils.isempty(imagebase64)) {
inputstream in = base64toinputstream(imagebase64);
string newname = system.currenttimemillis() + "_" + imagename + ".jpg";
string year = string.valueof(new date().getyear());
string month = string.valueof(new date().getmonth());
return uploadfile(bucketname, year + "/" + month + "/" + newname, in);
}
return null;
}
// base64decoder在jdk8以上的版本移除了,报错最简单解决换成jdk8就行了
public static inputstream base64toinputstream(string base64) {
bytearrayinputstream stream = null;
try {
byte[] bytes = new base64decoder().decodebuffer(base64.trim());
stream = new bytearrayinputstream(bytes);
} catch (exception e) {
e.printstacktrace();
}
return stream;
}
/**
* 上传本地文件
*
* @param bucketname 存储桶
* @param objectname 对象名称
* @param filename 本地文件路径
* @return
*/
@sneakythrows(exception.class)
public objectwriteresponse uploadfile(string bucketname, string objectname, string filename) {
return minioclient.uploadobject(
uploadobjectargs.builder()
.bucket(bucketname)
.object(objectname)
.filename(filename)
.build());
}
/**
* 通过流上传文件
*
* @param bucketname 存储桶
* @param objectname 文件对象
* @param inputstream 文件流
* @return
*/
@sneakythrows(exception.class)
public objectwriteresponse uploadfile(string bucketname, string objectname, inputstream inputstream) {
return minioclient.putobject(
putobjectargs.builder()
.bucket(bucketname)
.object(objectname)
.stream(inputstream, inputstream.available(), -1)
.build());
}
/**
* 创建文件夹或目录
*
* @param bucketname 存储桶
* @param objectname 目录路径
* @return
*/
@sneakythrows(exception.class)
public objectwriteresponse createdir(string bucketname, string objectname) {
return minioclient.putobject(
putobjectargs.builder()
.bucket(bucketname)
.object(objectname)
.stream(new bytearrayinputstream(new byte[]{}), 0, -1)
.build());
}
/**
* 获取文件信息, 如果抛出异常则说明文件不存在
*
* @param bucketname 存储桶
* @param objectname 文件名称
* @return
*/
@sneakythrows(exception.class)
public string getfilestatusinfo(string bucketname, string objectname) {
return minioclient.statobject(
statobjectargs.builder()
.bucket(bucketname)
.object(objectname)
.build()).tostring();
}
/**
* 拷贝文件
*
* @param bucketname 存储桶
* @param objectname 文件名
* @param srcbucketname 目标存储桶
* @param srcobjectname 目标文件名
*/
@sneakythrows(exception.class)
public objectwriteresponse copyfile(string bucketname, string objectname, string srcbucketname, string srcobjectname) {
return minioclient.copyobject(
copyobjectargs.builder()
.source(copysource.builder().bucket(bucketname).object(objectname).build())
.bucket(srcbucketname)
.object(srcobjectname)
.build());
}
/**
* 删除文件
*
* @param bucketname 存储桶
* @param objectname 文件名称
*/
@sneakythrows(exception.class)
public void removefile(string bucketname, string objectname) {
minioclient.removeobject(
removeobjectargs.builder()
.bucket(bucketname)
.object(objectname)
.build());
}
/**
* 批量删除文件
*
* @param bucketname 存储桶
* @param keys 需要删除的文件列表
* @return
*/
public void removefiles(string bucketname, list<string> keys) {
list<deleteobject> objects = new linkedlist<>();
keys.foreach(s -> {
objects.add(new deleteobject(s));
try {
removefile(bucketname, s);
} catch (exception e) {
log.error("[minio工具类]>>>> 批量删除文件,异常:", e);
}
});
}
/**
* 获取文件外链
*
* @param bucketname 存储桶
* @param objectname 文件名
* @param expires 过期时间 <=7 秒 (外链有效时间(单位:秒))
* @return url
*/
@sneakythrows(exception.class)
public string getpresignedobjecturl(string bucketname, string objectname, integer expires) {
getpresignedobjecturlargs args = getpresignedobjecturlargs.builder().expiry(expires).bucket(bucketname).object(objectname).build();
return minioclient.getpresignedobjecturl(args);
}
/**
* 获得文件外链
*
* @param bucketname
* @param objectname
* @return url
*/
@sneakythrows(exception.class)
public string getpresignedobjecturl(string bucketname, string objectname) {
getpresignedobjecturlargs args = getpresignedobjecturlargs.builder()
.bucket(bucketname)
.object(objectname)
.method(method.get).build();
return minioclient.getpresignedobjecturl(args);
}
/**
* 将urldecoder编码转成utf8
*
* @param str
* @return
* @throws unsupportedencodingexception
*/
public string getutf8byurldecoder(string str) throws unsupportedencodingexception {
string url = str.replaceall("%(?![0-9a-fa-f]{2})", "%25");
return urldecoder.decode(url, "utf-8");
}
}
5.5、创建controller
/**
* @author haoyang
* @create 2023-12-01 20:38
*/
@slf4j
@restcontroller
@requestmapping("/oss")
public class osscontroller {
@autowired
private minioutils minioutils;
@autowired
private minioconfig minioconfig;
/**
* 文件上传
*
* @param file
*/
@postmapping("/upload")
public string upload(@requestparam("file") multipartfile file) {
try {
//文件名
string filename = file.getoriginalfilename();
string newfilename = system.currenttimemillis() + "." + stringutils.substringafterlast(filename, ".");
//类型
string contenttype = file.getcontenttype();
minioutils.uploadfile(minioconfig.getbucketname(), file, newfilename, contenttype);
return "上传成功";
} catch (exception e) {
log.error("上传失败");
return "上传失败";
}
}
/**
* 删除
*
* @param filename
*/
@deletemapping("/")
public void delete(@requestparam("filename") string filename) {
minioutils.removefile(minioconfig.getbucketname(), filename);
}
/**
* 获取文件信息
*
* @param filename
* @return
*/
@getmapping("/info")
public string getfilestatusinfo(@requestparam("filename") string filename) {
return minioutils.getfilestatusinfo(minioconfig.getbucketname(), filename);
}
/**
* 获取文件外链
*
* @param filename
* @return
*/
@getmapping("/url")
public string getpresignedobjecturl(@requestparam("filename") string filename) {
return minioutils.getpresignedobjecturl(minioconfig.getbucketname(), filename);
}
/**
* 文件下载
*
* @param filename
* @param response
*/
@getmapping("/download")
public void download(@requestparam("filename") string filename, httpservletresponse response) {
try {
inputstream fileinputstream = minioutils.getobject(minioconfig.getbucketname(), filename);
response.setheader("content-disposition", "attachment;filename=" + filename);
response.setcontenttype("application/force-download");
response.setcharacterencoding("utf-8");
ioutils.copy(fileinputstream, response.getoutputstream());
} catch (exception e) {
log.error("下载失败");
}
}
}
6、测试验证
6.1、文件上传
使用postman调用http://localhost:3333/oss/upload 接口,选择某个文件测试上传功能,如下图所示:

6.2、文件下载
在浏览器中,调用http://localhost:3333/oss/download?filename=1701436432918.gif 接口,验证文件下载接口,如下图所示:

以上就是docker搭建minio容器的流程步骤的详细内容,更多关于docker搭建minio容器的资料请关注代码网其它相关文章!
发表评论