使用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博客。
目录
相关文章
java minio 8.x 通过https连接minio
java minio 8.x 通过https连接minio
1418 0
|
2天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
14天前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
96 38
|
11天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
2天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
2天前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
9 3
|
1天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
7 1
|
2天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
2天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
19 1
|
6天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
下一篇
无影云桌面