• 使用Java 操作MinIO


    概述

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

    今天我们使用JAVA来操作一下MinIO。

    Docker 安装MinIO

    • 创建目录和赋予权限

    mkdir -p /app/cloud/minio/data
    mkdir -p /app/cloud/minio/config
    chmod -R 777 /app/cloud/minio/data
    chmod -R 777 /app/cloud/minio/config
    
    • 拉取镜像docker pull minio:minio

    • 创建容器

    docker run -d -p 9000:9000 --name minio 
    -e "MINIO_ACCESS_KEY=minio" 
    -e "MINIO_SECRET_KEY=Aa123456" 
    -v /app/cloud/minio/data:/data 
    -v /app/cloud/minio/config:/root/.minio 
    minio/minio server /data
    
    • 浏览器访问http://192.168.1.6:9000账号 : minio 密码:Aa123456 登录右下角加号创建mybucket桶

    image.png
    • 开放 mybucket  读写权限

    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=80
    spring.application.name=book-minio
    
    spring.thymeleaf.cache=false
    
    spring.servlet.multipart.max-file-size = 10MB
    spring.servlet.multipart.max-request-size=100MB
    
    minio.endpoint=http://192.168.1.6:9000
    minio.accesskey=minio
    minio.secretKey=Aa123456
    
    • 连接 MinIO 配置

    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    @Data
    @ConfigurationProperties(prefix = "minio")
    @Component
    public class MinioProp {
        private String endpoint;
        private String accesskey;
        private String secretKey;
    }
    
    • 创建 MinioClient

    import io.minio.MinioClient;
    import io.minio.errors.InvalidEndpointException;
    import io.minio.errors.InvalidPortException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class MinioConfiguration {
        @Autowired
        private MinioProp minioProp;
    
        @Bean
        public MinioClient minioClient() throws InvalidPortException, InvalidEndpointException {
            MinioClient client = new MinioClient(minioProp.getEndpoint(), minioProp.getAccesskey(), minioProp.getSecretKey());
            return client;
        }
    }
    
    • MinIO 查看桶列表,存入,删除 操作 MinioController

    import com.alibaba.fastjson.JSON;
    import com.lab.book.minio.common.Res;
    import io.minio.MinioClient;
    import io.minio.ObjectStat;
    import io.minio.PutObjectOptions;
    import io.minio.Result;
    import io.minio.messages.Item;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.io.IOUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.ui.ModelMap;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URLEncoder;
    import java.text.DecimalFormat;
    import java.util.*;
    
    @Slf4j
    @RestController
    public class MinioController {
        @Autowired
        private MinioClient minioClient;
    
        private static final String MINIO_BUCKET = "mybucket";
    
        @GetMapping("/list")
        public List<Object> list(ModelMap map) throws Exception {
            Iterable<Result<Item>> myObjects = minioClient.listObjects(MINIO_BUCKET);
            Iterator<Result<Item>> iterator = myObjects.iterator();
            List<Object> items = new ArrayList<>();
            String format = "{'fileName':'%s','fileSize':'%s'}";
            while (iterator.hasNext()) {
                Item item = iterator.next().get();
                items.add(JSON.parse(String.format(format, item.objectName(), formatFileSize(item.size()))));
            }
            return items;
        }
    
        @PostMapping("/upload")
        public Res upload(@RequestParam(name = "file", required = false) MultipartFile[] file) {
            Res res = new Res();
            res.setCode(500);
    
            if (file == null || file.length == 0) {
                res.setMessage("上传文件不能为空");
                return res;
            }
    
            List<String> orgfileNameList = new ArrayList<>(file.length);
    
            for (MultipartFile multipartFile : file) {
                String orgfileName = multipartFile.getOriginalFilename();
                orgfileNameList.add(orgfileName);
    
                try {
                    InputStream in = multipartFile.getInputStream();
                    minioClient.putObject(MINIO_BUCKET, orgfileName, in, new PutObjectOptions(in.available(), -1));
                    in.close();
                } catch (Exception e) {
                    log.error(e.getMessage());
                    res.setMessage("上传失败");
                    return res;
                }
            }
    
            Map<String, Object> data = new HashMap<String, Object>();
            data.put("bucketName", MINIO_BUCKET);
            data.put("fileName", orgfileNameList);
            res.setCode(200);
            res.setMessage("上传成功");
            res.setData(data);
            return res;
        }
    
        @RequestMapping("/download/{fileName}")
        public void download(HttpServletResponse response, @PathVariable("fileName") String fileName) {
            InputStream in = null;
            try {
                ObjectStat stat = 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 (Exception e) {
                log.error(e.getMessage());
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    }
                }
            }
        }
    
        @DeleteMapping("/delete/{fileName}")
        public Res delete(@PathVariable("fileName") String fileName) {
            Res res = new Res();
            res.setCode(200);
            try {
                minioClient.removeObject(MINIO_BUCKET, fileName);
            } catch (Exception e) {
                res.setCode(500);
                log.error(e.getMessage());
            }
            return res;
        }
    
        private static String formatFileSize(long fileS) {
            DecimalFormat df = new DecimalFormat("#.00");
            String fileSizeString = "";
            String wrongSize = "0B";
            if (fileS == 0) {
                return wrongSize;
            }
            if (fileS < 1024) {
                fileSizeString = df.format((double) fileS) + " B";
            } else if (fileS < 1048576) {
                fileSizeString = df.format((double) fileS / 1024) + " KB";
            } else if (fileS < 1073741824) {
                fileSizeString = df.format((double) fileS / 1048576) + " MB";
            } else {
                fileSizeString = df.format((double) fileS / 1073741824) + " GB";
            }
            return fileSizeString;
        }
    }
    
    • Res 文件

    import lombok.AllArgsConstructor;
    import lombok.NoArgsConstructor;
    
    import java.io.Serializable;
    
    @lombok.Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Res implements Serializable {
        private static final long serialVersionUID = 1L;
        private Integer code;
        private Object data = "";
        private String message = "";
    }
    
    • 路由文件 RouterController

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    
    @Controller
    public class RouterController {
        @GetMapping({"/", "/index.html"})
        public String index() {
            return "index";
        }
    
        @GetMapping({"/upload.html"})
        public String upload() {
            return "upload";
        }
    }
    
    • 前端 列表页面 srcmain esources emplatesindex.html

    <!DOCTYPE html>
    <html lang="zh-cn">
    <head>
        <meta charset="utf-8"/>
        <title>图片列表</title>
        <link rel="stylesheet" href="http://cdn.staticfile.org/element-ui/2.13.1/theme-chalk/index.css">
    </head>
    <body>
    
    <div id="app">
    
        <el-link icon="el-icon-upload" href="/upload.html">上传图片</el-link>
        <br/>
    
        <el-table :data="results" stripe style=" 60%" @row-click="preview">
            <el-table-column type="index" width="50"></el-table-column>
            <el-table-column prop="fileName" label="文件名" width="180"></el-table-column>
            <el-table-column prop="fileSize" label="文件大小"></el-table-column>
            <el-table-column label="操作">
                <template slot-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-link icon="el-icon-picture">预览图片</el-link>
        <br/>
        <div class="demo-image__preview" v-if="previewImg">
            <el-image style=" 100px; height: 100px" :src="imgSrc" :preview-src-list="imgList"></el-image>
        </div>
    
    </div>
    
    <script src="http://cdn.staticfile.org/vue/2.6.11/vue.min.js"></script>
    <script src="http://cdn.staticfile.org/axios/0.19.2/axios.min.js"></script>
    <script src="http://cdn.staticfile.org/element-ui/2.13.1/index.js"></script>
    
    <script>
        new Vue({
            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 (var i = 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>
    
    • 前端上传页面 srcmain esources emplatesupload.html

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>图片上传</title>
        <link rel="stylesheet" type="text/css" href="http://cdn.staticfile.org/webuploader/0.1.5/webuploader.css">
        <script type="text/javascript" src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
        <script type="text/javascript" src="http://cdn.staticfile.org/webuploader/0.1.5/webuploader.min.js"></script>
    </head>
    
    <body>
    
    <div id="uploader-demo">
        <div id="fileList" class="uploader-list"></div>
        <div id="filePicker">选择图片</div>
    </div>
    <br/>
    <a href="/index.html">返回图片列表页面</a>
    
    <script type="text/javascript">
        var uploader = 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>
    

    运行项目

    image.png
    • 上传页面,批量上传图片

    image.png
    • 上传效果

    image.png
    • 查看 MinIO Browser

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

    image.png
    • 预览图片

    image.png
    • 删除图片

    image.png
    • 下载图片

    image.png

    Java 操作 MinIO 官方 demo

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

    好了,各位朋友们,本期的内容到此就全部结束啦,能看到这里的同学都是优秀的同学,下一个升职加薪的就是你了!
    如果觉得这篇文章对你有所帮助的话请扫描下面二维码加个关注。"
    转发" 加 "在看",养成好习惯!咱们下期再见!

     

    热文推荐

    ☞ 数据库优化之SQL优化
    ☞ 数据库优化之实例优化
    ☞ Docker基础与实战,看这一篇就够了!
    ☞ Docker-Compose基础与实战,看这一篇就够了!
     OAuth2.0最简向导(多图预警)
     构建三维一体立体化监控体系
    ☞ SpringCloud实战系列

    JAVA日知录

    长按左边二维码关注我们,精彩文章第一时间推送

            >>>技术交流群<<<

    朕已阅 

  • 相关阅读:
    现在不知道为什么安装pip包总是失败,只能用清华源
    linux 下 svn配置;以及多仓库配置
    谷歌浏览器安装json格式化插件
    RESTful API的理解
    mysql5.6 rpm安装配置
    linux,apache,php,mysql常用的查看版本信息的方法
    mysql允许别人通过ip访问本机mysql数据
    直接取PHP二维数组里面的值
    mysql优化
    self this
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13295886.html
Copyright © 2020-2023  润新知