使用Java 操作MinIO

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,恶意文件检测 1000次 1年
对象存储 OSS,内容安全 1000次 1年
简介: 今天我们使用JAVA来操作一下MinIO。

概述

MinIO 是一款高性能、分布式的对象存储系统。它是一款软件产品, 可以100%的运行在标准硬件。即X86等低成本机器也能够很好的运行MinIO。MinIO与传统的存储和其他的对象存储不同的是:它一开始就针对性能要求更高的私有云标准进行软件架构设计。因为MinIO一开始就只为对象存储而设计。所以他采用了更易用的方式进行设计,它能实现对象存储所需要的全部功能,在性能上也更加强劲,它不会为了更多的业务功能而妥协,失去MinIO的易用性、高效性。这样的结果所带来的好处是:它能够更简单的实现局有弹性伸缩能力的原生对象存储服务。MinIO在传统对象存储用例(例如辅助存储,灾难恢复和归档)方面表现出色。同时,它在机器学习、大数据、私有云、混合云等方面的存储技术上也独树一帜。当然,也不排除数据分析、高性能应用负载、原生云的支持。


Docker 安装MinIO


  • 创建目录和赋予权限


mkdir-p/app/cloud/minio/datamkdir-p/app/cloud/minio/configchmod-R777/app/cloud/minio/datachmod-R777/app/cloud/minio/config


  • 拉取镜像docker pull minio:minio


  • 创建容器


dockerrun-d-p9000:9000--nameminio\-e"MINIO_ACCESS_KEY=minio"\-e"MINIO_SECRET_KEY=Aa123456"\-v/app/cloud/minio/data:/data\-v/app/cloud/minio/config:/root/.minio\minio/minioserver/data



1.png


image.png


  • 开放 mybucket  读写权限


2.png


image.png


创建项目 操作 MinIO


  • pom.xml 相关依赖


<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>LATEST</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>7.0.1</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency>


  • 编辑配置文件application.properties修改MinIO相关配置


server.port=80spring.application.name=book-miniospring.thymeleaf.cache=falsespring.servlet.multipart.max-file-size=10MBspring.servlet.multipart.max-request-size=100MBminio.endpoint=http://192.168.1.6:9000minio.accesskey=miniominio.secretKey=Aa123456


  • 连接 MinIO 配置


importlombok.Data;
importorg.springframework.boot.context.properties.ConfigurationProperties;
importorg.springframework.stereotype.Component;
@Data@ConfigurationProperties(prefix="minio")
@ComponentpublicclassMinioProp {
privateStringendpoint;
privateStringaccesskey;
privateStringsecretKey;
}


  • 创建 MinioClient


importio.minio.MinioClient;
importio.minio.errors.InvalidEndpointException;
importio.minio.errors.InvalidPortException;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
@ConfigurationpublicclassMinioConfiguration {
@AutowiredprivateMinioPropminioProp;
@BeanpublicMinioClientminioClient() throwsInvalidPortException, InvalidEndpointException {
MinioClientclient=newMinioClient(minioProp.getEndpoint(), minioProp.getAccesskey(), minioProp.getSecretKey());
returnclient;
    }
}


  • MinIO 查看桶列表,存入,删除 操作 MinioController


importcom.alibaba.fastjson.JSON;
importcom.lab.book.minio.common.Res;
importio.minio.MinioClient;
importio.minio.ObjectStat;
importio.minio.PutObjectOptions;
importio.minio.Result;
importio.minio.messages.Item;
importlombok.extern.slf4j.Slf4j;
importorg.apache.commons.io.IOUtils;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.ui.ModelMap;
importorg.springframework.web.bind.annotation.*;
importorg.springframework.web.multipart.MultipartFile;
importjavax.servlet.http.HttpServletResponse;
importjava.io.IOException;
importjava.io.InputStream;
importjava.net.URLEncoder;
importjava.text.DecimalFormat;
importjava.util.*;
@Slf4j@RestControllerpublicclassMinioController {
@AutowiredprivateMinioClientminioClient;
privatestaticfinalStringMINIO_BUCKET="mybucket";
@GetMapping("/list")
publicList<Object>list(ModelMapmap) throwsException {
Iterable<Result<Item>>myObjects=minioClient.listObjects(MINIO_BUCKET);
Iterator<Result<Item>>iterator=myObjects.iterator();
List<Object>items=newArrayList<>();
Stringformat="{'fileName':'%s','fileSize':'%s'}";
while (iterator.hasNext()) {
Itemitem=iterator.next().get();
items.add(JSON.parse(String.format(format, item.objectName(), formatFileSize(item.size()))));
        }
returnitems;
    }
@PostMapping("/upload")
publicResupload(@RequestParam(name="file", required=false) MultipartFile[] file) {
Resres=newRes();
res.setCode(500);
if (file==null||file.length==0) {
res.setMessage("上传文件不能为空");
returnres;
        }
List<String>orgfileNameList=newArrayList<>(file.length);
for (MultipartFilemultipartFile : file) {
StringorgfileName=multipartFile.getOriginalFilename();
orgfileNameList.add(orgfileName);
try {
InputStreamin=multipartFile.getInputStream();
minioClient.putObject(MINIO_BUCKET, orgfileName, in, newPutObjectOptions(in.available(), -1));
in.close();
            } catch (Exceptione) {
log.error(e.getMessage());
res.setMessage("上传失败");
returnres;
            }
        }
Map<String, Object>data=newHashMap<String, Object>();
data.put("bucketName", MINIO_BUCKET);
data.put("fileName", orgfileNameList);
res.setCode(200);
res.setMessage("上传成功");
res.setData(data);
returnres;
    }
@RequestMapping("/download/{fileName}")
publicvoiddownload(HttpServletResponseresponse, @PathVariable("fileName") StringfileName) {
InputStreamin=null;
try {
ObjectStatstat=minioClient.statObject(MINIO_BUCKET, fileName);
response.setContentType(stat.contentType());
response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8"));
in=minioClient.getObject(MINIO_BUCKET, fileName);
IOUtils.copy(in, response.getOutputStream());
        } catch (Exceptione) {
log.error(e.getMessage());
        } finally {
if (in!=null) {
try {
in.close();
                } catch (IOExceptione) {
log.error(e.getMessage());
                }
            }
        }
    }
@DeleteMapping("/delete/{fileName}")
publicResdelete(@PathVariable("fileName") StringfileName) {
Resres=newRes();
res.setCode(200);
try {
minioClient.removeObject(MINIO_BUCKET, fileName);
        } catch (Exceptione) {
res.setCode(500);
log.error(e.getMessage());
        }
returnres;
    }
privatestaticStringformatFileSize(longfileS) {
DecimalFormatdf=newDecimalFormat("#.00");
StringfileSizeString="";
StringwrongSize="0B";
if (fileS==0) {
returnwrongSize;
        }
if (fileS<1024) {
fileSizeString=df.format((double) fileS) +" B";
        } elseif (fileS<1048576) {
fileSizeString=df.format((double) fileS/1024) +" KB";
        } elseif (fileS<1073741824) {
fileSizeString=df.format((double) fileS/1048576) +" MB";
        } else {
fileSizeString=df.format((double) fileS/1073741824) +" GB";
        }
returnfileSizeString;
    }
}


  • Res 文件


importlombok.AllArgsConstructor;
importlombok.NoArgsConstructor;
importjava.io.Serializable;
@lombok.Data@AllArgsConstructor@NoArgsConstructorpublicclassResimplementsSerializable {
privatestaticfinallongserialVersionUID=1L;
privateIntegercode;
privateObjectdata="";
privateStringmessage="";
}


  • 路由文件 RouterController


importorg.springframework.stereotype.Controller;
importorg.springframework.web.bind.annotation.GetMapping;
@ControllerpublicclassRouterController {
@GetMapping({"/", "/index.html"})
publicStringindex() {
return"index";
    }
@GetMapping({"/upload.html"})
publicStringupload() {
return"upload";
    }
}


  • 前端 列表页面 src\main\resources\templates\index.html


<!DOCTYPEhtml><htmllang="zh-cn"><head><metacharset="utf-8"/><title>图片列表</title><linkrel="stylesheet"href="http://cdn.staticfile.org/element-ui/2.13.1/theme-chalk/index.css"></head><body><divid="app"><el-linkicon="el-icon-upload"href="/upload.html">上传图片</el-link><br/><el-table :data="results"stripestyle="width: 60%"@row-click="preview"><el-table-columntype="index"width="50"></el-table-column><el-table-columnprop="fileName"label="文件名"width="180"></el-table-column><el-table-columnprop="fileSize"label="文件大小"></el-table-column><el-table-columnlabel="操作"><templateslot-scope="scope"><a :href="'/download/' + scope.row.fileName + ''"class="el-icon-download">下载</a><a :href="'/delete/' + scope.row.fileName + ''"@click.prevent="deleteFile($event,scope.$index,results)"class="el-icon-delete">删除</a></template></el-table-column></el-table><br/><el-linkicon="el-icon-picture">预览图片</el-link><br/><divclass="demo-image__preview"v-if="previewImg"><el-imagestyle="width: 100px; height: 100px" :src="imgSrc" :preview-src-list="imgList"></el-image></div></div><scriptsrc="http://cdn.staticfile.org/vue/2.6.11/vue.min.js"></script><scriptsrc="http://cdn.staticfile.org/axios/0.19.2/axios.min.js"></script><scriptsrc="http://cdn.staticfile.org/element-ui/2.13.1/index.js"></script><script>newVue({
el: '#app',
data: {
bucketURL: 'http://192.168.1.6:9000/mybucket/',
previewImg: true,
results: [],
imgSrc: '',
imgList: []
        },
methods: {
init() {
axios.get('/list').then(response=> {
this.results=response.data;
if (this.results.length==0) {
this.imgSrc='';
this.previewImg=false;
                    } else {
for (vari=0; i<this.results.length; i++) {
this.imgList.push(this.bucketURL+this.results[i].fileName);
if (i==0) {
this.imgSrc=this.bucketURL+this.results[0].fileName;
                            }
                        }
                    }
                });
            },
preview(row, event, column) {
this.imgSrc=this.bucketURL+row.fileName;
this.previewImg=true;
            },
deleteFile(e,index,list) {
axios.delete(e.target.href, {}).then(res=> {
if (res.data.code==200) {
this.$message('删除成功!');
list.splice(index, 1);
this.previewImg=false;
                    } else {
this.$message('删除失败!');
                    }
                });
            }
        },
mounted() {
this.init();
        }
    });
</script></body></html>


  • 前端上传页面 src\main\resources\templates\upload.html


<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>图片上传</title><linkrel="stylesheet"type="text/css"href="http://cdn.staticfile.org/webuploader/0.1.5/webuploader.css"><scripttype="text/javascript"src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script><scripttype="text/javascript"src="http://cdn.staticfile.org/webuploader/0.1.5/webuploader.min.js"></script></head><body><divid="uploader-demo"><divid="fileList"class="uploader-list"></div><divid="filePicker">选择图片</div></div><br/><ahref="/index.html">返回图片列表页面</a><scripttype="text/javascript">varuploader=WebUploader.create({
auto: true,
swf: 'http://cdn.staticfile.org/webuploader/0.1.5/Uploader.swf',
server: '/upload',
pick: '#filePicker',
accept: {
title: 'Images',
extensions: 'gif,jpg,jpeg,bmp,png',
mimeTypes: 'image/*'        }
    });
uploader.on('fileQueued', function (file) {
var$li=$(
'<div id="'+file.id+'" class="file-item thumbnail">'+'<img>'+'<div class="info">'+file.name+'</div>'+'</div>'            ),
$img=$li.find('img');
var$list=$("#fileList");
$list.append($li);
uploader.makeThumb(file, function (error, src) {
if (error) {
$img.replaceWith('<span>不能预览</span>');
return;
            }
$img.attr('src', src);
        }, 100, 100);
    });
uploader.on('uploadProgress', function (file, percentage) {
var$li=$('#'+file.id),
$percent=$li.find('.progress span');
if (!$percent.length) {
$percent=$('<p class="progress"><span></span></p>')
                .appendTo($li)
                .find('span');
        }
$percent.css('width', percentage*100+'%');
    });
uploader.on('uploadSuccess', function (file) {
$('#'+file.id).addClass('upload-state-done');
    });
uploader.on('uploadError', function (file) {
var$li=$('#'+file.id),
$error=$li.find('div.error');
if (!$error.length) {
$error=$('<div class="error"></div>').appendTo($li);
        }
$error.text('上传失败');
    });
uploader.on('uploadComplete', function (file) {
$('#'+file.id).find('.progress').remove();
    });
</script></body></html>


运行项目


3.png


image.png


  • 上传页面,批量上传图片


4.png


image.png


  • 上传效果


5.png


image.png


  • 查看 MinIO Browser


6.png


image.png

  • 列表页面,下载,删除,预览操作


aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9FbnpBd3ZwVFFKN1NHM0lYUmliWlVZaWJOVXpjM3dZeWliV0NmYzNDd2liUEF5ZGFkV095eFh0MTVUaWJMTFVKOGhjQ09SeEZSeTBtVVRCQnkxS1lGakNqaG5nLzY0MA.png


image.png


  • 预览图片


8.png


image.png


  • 删除图片


9.png


image.png


  • 下载图片


10.png


image.png


Java 操作 MinIO 官方 demo

https://github.com/minio/minio-java/tree/master/examples

相关实践学习
Serverless极速搭建Hexo博客
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
目录
相关文章
|
存储 分布式计算 安全
分布式文件系统介绍与minio介绍与使用(附minio java client 使用)(一)
分布式文件系统介绍与minio介绍与使用(附minio java client 使用)
463 0
|
3月前
|
Java API 开发者
Java中的文件I/O操作详解
Java中的文件I/O操作详解
|
Oracle Java 关系型数据库
Java 安装与配置
JDK 是 Java 软件开发工具包,本文以 JDK 1.8 版本为例,分别介绍了在 Windows 和 Linux 系统下, JDK 的安装与环境配置过程。 https://ciilii.com/question/241
100 0
|
存储 算法 网络协议
分布式文件系统介绍与minio介绍与使用(附minio java client 使用)(二)
分布式文件系统介绍与minio介绍与使用(附minio java client 使用)
576 0
|
Java 存储 Linux
分布式文件系统介绍与minio介绍与使用(附minio java client 使用)(三)
分布式文件系统介绍与minio介绍与使用(附minio java client 使用)
178 0
|
存储 机器学习/深度学习 弹性计算
使用Java 操作MinIO
使用Java 操作MinIO
728 0
|
Java Linux
java命令 : java -jar 和 java -cp
java命令 : java -jar 和 java -cp
1234 0
|
Java 应用服务中间件 Linux
Java 在linux或者tomcat下使用java.jwt.*这个类,报java.awt.headless 报空异常
在开发的过程中使用到了java.jwt.*包下的东西,在开发工具中使用没问题,但是如果到了单独的tomcat或Linux里就会报:java.awt.headless null空异常,再去配置java mv?非常麻烦,看我是如何解决的。
126 0
|
Java
Java报错:Cause: java.io.NotSerializableException: xxx 解决方案
原因是实体类没有开启序列化接口导致的 开启实体类序列化接口即可:
239 0
|
Java Windows
【Java】JAVA_HOME环境变量的配置(二)
本期主要介绍JAVA_HOME环境变量的配置
307 0
【Java】JAVA_HOME环境变量的配置(二)
下一篇
无影云桌面