minio获取上传文件_SpringBoot 系列教程 一百零四 MinIo文件服务

在前面的一篇文章中,详细的给大家介绍了如何在Centos7.x环境下从零开始搭建分布式文件系统MinIo,错过的朋友可以点这里:从零搭建分布式文件系统MinIO比FastDFS要更合适

那么继上一篇文章之后,本篇文章将带领大家开始实战,选用当下主流的SpringBoot2.x框架来整合分布式文件系统MinIo,关于MinIo的使用,接下来我们一探究竟。

一、SpringBoot整合MinIo

关于SpringBoot整合MinIo 以及Minio相关操作的API可参考官方文档,官方文档给出了详细的API解释:

参考: http://docs.minio.org.cn/docs/master/java-client-quickstart-guide

参考: https://docs.min.io/cn/java-client-api-reference.html

Minio支持接入JavaScript、Java、Python、Golang等多种语言,这里我们选择最熟悉的Java语言,使用最流行的框架 SpringBoot 2.x。

  • 完整项目目录如下:
minio获取上传文件_SpringBoot 系列教程 一百零四 MinIo文件服务_第1张图片

完整项目结构图

1. 引入minio依赖

    io.minio    minio    ${minio.version}

2. 完整的Pom.xml

    4.0.0            org.springframework.boot        spring-boot-starter-parent        2.4.0                 com.example    springboot-minio    0.0.1-SNAPSHOT    springboot-minio    SpringBoot 系列教程(一百零四):SpringBoot 整合 MinIo 文件服务            1.8                                    org.springframework.boot            spring-boot-starter-web                                    org.projectlombok            lombok            true                                    org.springframework.boot            spring-boot-starter-test            test                                    io.minio            minio            7.0.2                                    com.alibaba            fastjson            1.2.7                                    org.springframework.boot            spring-boot-starter-thymeleaf                                    org.apache.commons            commons-lang3            3.9                                    cn.hutool            hutool-core            5.4.4                                                    org.springframework.boot                spring-boot-maven-plugin                        

3. application.yml

这时候需要在application.yml文件中配置关于minio文件服务器的相关配置参数

#服务端口server:  port: 8899#minio配置minio:  #minio的服务端访问地址  endpoint: http://192.168.15.135:9000  #账号  accessKey: minioadmin  #密码  secretKey: minioadmin  #桶名称(根目录文件夹名称)  bucketName: dev

4. MinIoProperties

将minio的属性配置信息映射到Java配置类,并注册成Bean, 如下:

package com.example.minio.properties;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Configuration;/** * @desc:  minio的属性配置信息映射到Java配置类,并注册成Bean * @author: cao_wencao * @date: 2020-11-19 17:13 */@Data@Configuration@ConfigurationProperties(prefix = "minio")public class MinIoProperties {    /**     * 服务地址     */    private String endpoint;    /**     * 用户名     */    private String accessKey;    /**     * 密码     */    private String secretKey;    /**     * 桶名称     */    private String bucketName;}

5. MinIoUtils工具类

根据Minio官方API文档封装MinIo工具类,如下:

package com.example.minio.config;import com.example.minio.properties.MinIoProperties;import io.minio.MinioClient;import io.minio.PutObjectOptions;import io.minio.Result;import io.minio.messages.Bucket;import io.minio.messages.Item;import lombok.SneakyThrows;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import java.io.InputStream;import java.util.List;/** * http://docs.minio.org.cn/docs/master/java-client-api-reference * 参考:https://www.jianshu.com/p/7f493105b2b2 * * @desc: 根据Minio官方API文档封装MinIo工具类 * @author: cao_wencao * @date: 2020-11-19 17:16 */@Component@EnableConfigurationProperties(value = {MinIoProperties.class})@ConditionalOnProperty(prefix = "minio", name = {"endpoint", "accessKey", "secretKey", "bucketName"})public class MinIoUtils {    private final MinioClient minioClient;    private final String bucketName;    @Autowired    private MinIoProperties minIoProperties;    /**     * 初始化MinioClient     */    @SneakyThrows    public MinIoUtils(MinIoProperties minIoProperties) {        if (!StringUtils.hasText(minIoProperties.getBucketName())) {            throw new RuntimeException("bucket name can not be empty or null");        }        this.bucketName = StringUtils.trimTrailingCharacter(minIoProperties.getBucketName().trim(), '/');        minioClient = new MinioClient(minIoProperties.getEndpoint(), minIoProperties.getAccessKey(), minIoProperties.getSecretKey());    }    /**     * 判断bucket是否存在     *     * @param bucketName bucket名称     * @return     */    @SneakyThrows    public boolean bucketExists(String bucketName) {        return minioClient.bucketExists(bucketName);    }    /**     * 创建bucket     *     * @param bucketName bucket名称     */    @SneakyThrows    public void makeBucket(String bucketName) {        boolean isExist = minioClient.bucketExists(bucketName);        if (!isExist) {            minioClient.makeBucket(bucketName);        }    }    /**     * 获取全部bucket     */    @SneakyThrows    public List getAllBuckets() {        return minioClient.listBuckets();    }    /**     * 判断文件是否存在     *     * @param objectName 文件名称     * @return     */    public boolean objectExists(String objectName) {        boolean isExists = false;        try {            InputStream inputStream = minioClient.getObject(bucketName, objectName);            if (null != inputStream) {                isExists = true;            }            return isExists;        } catch (Exception e) {            throw new RuntimeException(e);        }    }    /**     * 获取单个文件     *     * @param objectName 文件名称     * @return     */    @SneakyThrows    public InputStream getObject(String objectName) {        return minioClient.getObject(bucketName, objectName);    }    /**     * 获取文件集合     *     * @param bucketN bucket名称     * @return     */    @SneakyThrows    public Iterable> listObjects(String bucketN) {        if (StringUtils.hasText(bucketN)) {            bucketN = bucketName;        }        return minioClient.listObjects(bucketN);    }    /**     * 根据prefix获取object集合     *     * @param prefix     * @return     */    @SneakyThrows    public Iterable> listObjectsByPrefix(String prefix) {        return minioClient.listObjects(bucketName, prefix);    }    /**     * 获取文件外链     *     * @param objectName 文件名称     * @return     */    @SneakyThrows    public String buildObjectUrl(String objectName) {        if (!StringUtils.hasText(objectName)) {            throw new RuntimeException("object name can not be empty or null");        }        objectName = StringUtils.trimLeadingCharacter(StringUtils.trimTrailingCharacter(objectName.trim(), '/'), '/');        String objectUrl = String.format("%s/%s", bucketName, objectName);        if (StringUtils.hasText(minIoProperties.getEndpoint())) {            objectUrl = String.format("%s/%s", minIoProperties.getEndpoint(), objectUrl);        }        return objectUrl;    }    /**     * 上传文件-InputStream     *     * @param bucketName bucket名称     * @param objectName 文件名称     * @param stream     文件流     */    @SneakyThrows    public void putObject(String bucketName, String objectName, InputStream stream) {        minioClient.putObject(bucketName, objectName, stream, new PutObjectOptions(stream.available(), -1));    }    /**     * 上传文件-InputStream     *     * @param objectName 文件名称     * @param stream     文件流     */    @SneakyThrows    public void putObject(String objectName, InputStream stream) {        minioClient.putObject(bucketName, objectName, stream, new PutObjectOptions(stream.available(), -1));    }    /**     * 上传文件     *     * @param objectName  文件名称     * @param stream      文件流     * @param size        大小     * @param contentType 类型     * @return the object url string     */    @SneakyThrows    public String putObject(String objectName, InputStream stream, Long size, String contentType) {        if (!StringUtils.hasText(objectName)) {            throw new RuntimeException("object name can not be empty or null");        }        minioClient.putObject(bucketName, objectName, stream, new PutObjectOptions(stream.available(), -1));        return buildObjectUrl(objectName);    }    /**     * 删除文件     *     * @param bucketName bucket名称     * @param objectName 文件名称     */    @SneakyThrows    public void removeObject(String bucketName, String objectName) {        minioClient.removeObject(bucketName, objectName);    }    /**     * 删除文件     *     * @param objectName 文件名称     */    @SneakyThrows    public void removeObject(String objectName) {        minioClient.removeObject(bucketName, objectName);    }}

6. MinioFileController

根据已经封装好的MinioUtils工具类,我们写几个简单的图片上传、图片删除、图片下载、获取图片信息、图片预览功能,如下Api接口所示:

package com.example.minio.controller;import cn.hutool.core.date.DateUtil;import com.example.minio.config.MinIoUtils;import com.example.minio.result.RestResponse;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.*;import org.springframework.web.multipart.MultipartFile;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletResponse;import java.io.ByteArrayOutputStream;import java.io.InputStream;/** * @desc: 文件上传工具类 * @author: cao_wencao * @date: 2020-11-19 22:00 */@RestController@Slf4jpublic class MinioFileController {    @Autowired    private MinIoUtils minIoUtils;    /**     * 创建资源桶     * @param bucketName     * @return     */    @GetMapping(value = "/createBucket")    public RestResponse createBucket(String bucketName){        if (!minIoUtils.bucketExists(bucketName)) {            minIoUtils.makeBucket(bucketName);            return RestResponse.success("桶创建成功");        }        return RestResponse.fail("该桶已存在,请勿重复创建");    }    /**     * @desc: 上传图片     * @auth: cao_wencao     * @date: 2020/11/19 22:22     */    @PostMapping(value = "uploadImg")    public RestResponse uploadImage(@RequestParam("file") MultipartFile file) throws Exception {        if (file.isEmpty()) {            throw new RuntimeException("上传文件不能为空");        } else {            // 得到文件流            final InputStream inputStream = file.getInputStream();            // 文件名            final String originalFilename = file.getOriginalFilename();            String filelName = "bucketName" + "_" +                    System.currentTimeMillis() +                    "wechat.jpg".substring("wechat.jpg".lastIndexOf("."));            String objectName = String.format("images/%s/%s", DateUtil.today(),+                    System.currentTimeMillis() +                    originalFilename.substring(originalFilename.lastIndexOf(".")));            log.info("【objectName的名称】:{}", objectName);            //String objectName = String.format("upload/images/%s/%s/%s", DateUtil.today(),            //        UUID.randomUUID().toString().replace("-", "").substring(0, 10),            //        originalFilename);            // 把文件放到minio的boots桶里面            minIoUtils.putObject(objectName, inputStream);            // 关闭输入流            inputStream.close();            return RestResponse.success("上传成功");        }    }    /**     * 获取图片的URL访问地址     * @param objectName     * @return     */    @RequestMapping("/getObjectURL")    public RestResponse getObjectURL(String objectName){        String url = minIoUtils.buildObjectUrl(objectName);        return RestResponse.success(url);    }    /**     * 删除文件     * @param objectName     * @return     */    @GetMapping(value = "removeImg")    public RestResponse removeImage(@RequestParam("objectName") String objectName) {        if (!StringUtils.hasText(objectName)){            throw new RuntimeException("资源名称不能为空");        }        minIoUtils.removeObject(objectName);        return RestResponse.success("删除成功");    }    /**     * 下载文件到本地     * @param objectName     * @param response     * @return     * @throws Exception     */    @GetMapping("/downloadImage")    public RestResponse downloadImage(@RequestParam("objectName") String objectName, HttpServletResponse response) throws Exception {        ResponseEntity responseEntity = null;        InputStream stream = null;        ByteArrayOutputStream output = null;        try {            // 获取"myobject"的输入流。            stream = minIoUtils.getObject(objectName);            if (stream == null) {                System.out.println("文件不存在");            }            //用于转换byte            output = new ByteArrayOutputStream();            byte[] buffer = new byte[4096];            int n = 0;            while (-1 != (n = stream.read(buffer))) {                output.write(buffer, 0, n);            }            byte[] bytes = output.toByteArray();            //设置header            HttpHeaders httpHeaders = new HttpHeaders();            httpHeaders.add("Accept-Ranges", "bytes");            httpHeaders.add("Content-Length", bytes.length + "");//            objectName = new String(objectName.getBytes("UTF-8"), "ISO8859-1");            //把文件名按UTF-8取出并按ISO8859-1编码,保证弹出窗口中的文件名中文不乱码,中文不要太多,最多支持17个中文,因为header有150个字节限制。            httpHeaders.add("Content-disposition", "attachment; filename=" + objectName);            httpHeaders.add("Content-Type", "text/plain;charset=utf-8");//            httpHeaders.add("Content-Type", "image/jpeg");            responseEntity = new ResponseEntity(bytes, httpHeaders, HttpStatus.CREATED);        } catch (Exception e) {            e.printStackTrace();        } finally {            if (stream != null) {                stream.close();            }            if (output != null) {                output.close();            }        }        return RestResponse.success(responseEntity);    }    /**     * 在浏览器预览图片,预览功能,需要设置contextType = “image/jpeg”     * @param objectName     * @param response     * @throws Exception     */    @RequestMapping("/preViewPicture")    public void preViewPicture(@RequestParam("objectName") String objectName, HttpServletResponse response) throws Exception{        response.setContentType("image/jpeg");        ServletOutputStream out = null;        try {            out = response.getOutputStream();            InputStream stream = minIoUtils.getObject(objectName);            ByteArrayOutputStream output = new ByteArrayOutputStream();            byte[] buffer = new byte[4096];            int n = 0;            while (-1 != (n = stream.read(buffer))) {                output.write(buffer, 0, n);            }            byte[] bytes = output.toByteArray();            out.write(bytes == null ? new byte[0]:bytes);            out.flush();        }finally {            if (out != null) {                out.close();            }        }    }}

7. REST接口工具类RestResponse

总所周知,Rest风格的API接口在返回数据到客户端时,需要统一返回数据格式,这既是规范也是基本要求,封装的 REST接口工具类如下:

package com.example.minio.result;import java.io.Serializable;/** * REST接口统一返回数据工具类封装RestResponse * @param  */public class RestResponse implements Serializable {    private static final long serialVersionUID = 3728877563912075885L;    private int code;    private String msg;    private T data;    public RestResponse(){    }    public RestResponse(int code, String message, T data) {        this.code = code;        this.setMsg(message);        this.data = data;    }    public RestResponse(int code, T data) {        this.code = code;        this.data = data;    }    public RestResponse(int code, String message) {        this.code = code;        this.setMsg(message);    }     /**     * 成功时-返回data     * @param      * @return     */    public static  RestResponse success(T data){        return new RestResponse(200, null, data);    }    /**     * 成功-不返回data     * @param      * @return     */    public static  RestResponse success(String msg){        return new RestResponse(200, msg);    }    /**     * 成功-返回data+msg     * @param      * @return     */    public static  RestResponse success(String msg, T data){        return new RestResponse(200, msg, data);    }     /**     * 失败     * @param      * @return     */    public static  RestResponse fail(String msg){        return new RestResponse(500, msg,null);    }    /**     * 失败-code     * @param      * @return     */    public static  RestResponse fail(int code, String msg){        return new RestResponse(code, msg,null);    }     public int getCode() {        return code;    }    public String getMsg() {        return msg;    }      public T getData() {        return data;    }    public void setCode(int code) {        this.code = code;    }    public void setMsg(String msg) {        this.msg = msg;    }    public void setData(T data) {        this.data = data;    }    @Override    public String toString() {        return "RestResponse{" + "code=" + code + ", msg='" + msg + ''' +", data=" + data +'}';    }}

二、文件上传页面

1. 简单页面

html>        测试minio上传    文件:    

2. 页面跳转

import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;/** * @desc:  页面跳转 * @author: cao_wencao * @date: 2020-11-20 10:53 */@Controllerpublic class PageController {    @RequestMapping("/toPage")    public String toPage(){     return "upload";    }}

三、简单测试

1. 上传图片(http://127.0.0.1:8899/toPage)

minio获取上传文件_SpringBoot 系列教程 一百零四 MinIo文件服务_第2张图片

在这里插入图片描述

访问上传图片的页面后,选择一张图片进行上传,上传完成后,登录控制台查看上传成功的文件如下所示:

minio获取上传文件_SpringBoot 系列教程 一百零四 MinIo文件服务_第3张图片

在这里插入图片描述

2. 预览功能

  • URL : http://127.0.0.1:8899/preViewPicture?objectName=images/2020-11-20/1605857319982.png
minio获取上传文件_SpringBoot 系列教程 一百零四 MinIo文件服务_第4张图片

四、参考文档

https://docs.min.io/cn/java-client-quickstart-guide.html

https://docs.min.io/cn/java-client-api-reference.html

五、源码

https://github.com/Thinkingcao/SpringBootLearning/tree/master/springboot-minio

你可能感兴趣的:(minio获取上传文件)