• 华为云云耀云服务器L实例评测|基于云服务器的minio部署手册


    华为云云耀云服务器L实例评测|基于云服务器的minio部署手册

    【软件安装版本】【集群安装(是)(否)】

                                     

    版本

    创建人

    修改人

    创建时间

    备注

    1.0

    jz

    jz

    2023.9.2

    minio华为云耀服务器

    一.  部署规划与架构

    1. 规划:(集群:网络规划,服务器规划)

      安装方式 :非集群安装

      服务器   :  华为云:192.168.0.147

      端口号   :A.minio管理界面:9000,33806 B.demo应用:8081

    2. 架构(集群:拓扑图)

       

      3. 支撑业务

      小文件服务器:图片,文件,视频的存储管理。

    二. 运行环境安装

    1. 硬件

       华为云耀服务器,2核2G 3m网络

         2. 操作系统

     uname -a

       为了springboot的demo运行,需要提前安装好Java运行环境。这里不再重述

        3. 环境配置

            软件安装路径:/opt/software/minio

            

    mkdir -p /opt/software/minio

       

       数据文件路径:/opt/data/minio

    mkdir -p /opt/data/minio

       日志文件路径: /opt/data/minio/logs

    mkdir -p /opt/data/minio/logs

     

    三. 单机部署步骤 

    1. 安装包获取与安装

      下载地址;https://min.io/download#/linux

    cd /opt/software/minio/

    wget https://dl.min.io/server/minio/release/linux-amd64/minio

     

    安装成功:

    查看下载文件:

    ls

         2. 配置修改

      

    vim /etc/default/minio

    # 启动的时候看提示 新版本

    MINIO_ROOT_USER=username

    MINIO_ROOT_PASSWORD=password

    # 如果MinIO版本比较旧,修改用户名密码为

    MINIO_ACCESS_KEY=username

    MINIO_SECRET_KEY=password

    注意:创建的文件是个新文件。

    修改环境变量

    vim /etc/profile

    增加两行

    export  MINIO_ROOT_USER=username

    export  MINIO_ROOT_PASSWORD=password

    # 如果MinIO版本比较旧,修改用户名密码为

    export MINIO_ACCESS_KEY=username

    export MINIO_SECRET_KEY=password

    配置文件生效

    source /etc/profile

     

        3. 检测依赖环境是否就绪

       

    java -version

        4.安装

       赋予文件安装权限:

       

    chmod 777 minio

     ls -l

     

    以守护进程方式启动,指定端口为9000固定端口,数据文件路径/opt/data/minio/

    nohup /opt/software/minio/minio server --console-address :33806 --address 0.0.0.0:9000 /opt/data/minio >  /opt/data/minio/logs/minio.log &

    ps -ef |grep minio

    more minio.log

     

    Minio启动成功

        5. 云服务器开通公网端口号:9000

       登录华为云账号:https://auth.huaweicloud.com/

    配置到服务器

    进入控制台,更改安全组

    选择刚才创建的安全组

    1. 验证

     浏览器输入:公网ip:9000

    会自动跳转到33806端口上。

    用username/password登录

     四.应用部署

        1. 安装java

      请自行安装jdk文件,保证java -version可用。

      2.编写项目

      Pom文件中增加配置



        io.minio
        minio
        ${minio.version}

    Yaml中增加配置

    minio:
      endpoint: http://192.168.0.213:9000 #MinIO服务所在地址
      bucketName: mall #存储桶名称
      bucketImg: img # 图片桶
      bucketVideo: video # 视频桶
      accessKey: fileadmin #访问的key
      secretKey: fileadmin #访问的秘钥

    编写工具类

    package top.fairy.global.globalfairytoppi4j.utils;

    import io.minio.*;

    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.stereotype.Component;
    import org.springframework.web.multipart.MultipartFile;
    import top.fairy.global.globalfairytoppi4j.beans.ResultEntity;
    import top.fairy.global.globalfairytoppi4j.config.MinioClientConfig;

    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletResponse;
    import java.io.InputStream;
    import java.nio.charset.StandardCharsets;
    import java.util.Map;


    @Slf4j
    @Component
    public class MinioUtil {

        /**
         * Minio文件上传
         *
         * @param file       文件实体
         * @param fileName   修饰过的文件名 非源文件名
         * @param bucketName 所存文件夹(桶名)
         * @return
         */
        public ResultEntity> minioUpload(MultipartFile file, String fileName, String bucketName) {
            ResultEntity> resultEntity = new ResultEntity();
            try {
                MinioClient minioClient = MinioClientConfig.getMinioClient();
                // fileName为空,说明要使用源文件名上传
                if (fileName == null) {
                    fileName = file.getOriginalFilename();
                    fileName = fileName.replaceAll(" ", "_");
                }
                InputStream inputStream = file.getInputStream();
                PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(fileName)
                        .stream(inputStream, file.getSize(), -1).contentType(file.getContentType()).build();
                //文件名称相同会覆盖
                minioClient.putObject(objectArgs);
                return resultEntity;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }


        /**
         * 检查存储桶是否存在
         *
         * @param bucketName 存储桶名称
         * @return
         */
        public boolean bucketExists(String bucketName) {
            boolean flag = false;
            try {
                flag = MinioClientConfig.bucketExists(bucketName);
                if (flag) {
                    return true;
                }
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
            return false;
        }

        /**
         * 获取文件流
         *
         * @param fileName   文件名
         * @param bucketName 桶名(文件夹)
         * @return
         */
        public InputStream getFileInputStream(String fileName, String bucketName) {
            try {
                MinioClient minioClient = MinioClientConfig.getMinioClient();
                return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
            } catch (Exception e) {
                e.printStackTrace();
                log.error(e.getMessage());
            }
            return null;
        }


        /**
         * @param bucketName:
         * @author
         * @description: 创建桶
         * @date 2022/8/16 14:36
         */
        public void createBucketName(String bucketName) {
            try {
                if (StringUtils.isBlank(bucketName)) {
                    return;
                }
                MinioClient minioClient = MinioClientConfig.getMinioClient();
                boolean isExist = MinioClientConfig.bucketExists(bucketName);
                if (isExist) {
                    log.info("Bucket {} already exists.", bucketName);
                } else {
                    minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error(e.getMessage());
            }
        }

        /**
         * 下载文件
         *
         * @param originalName 文件路径
         */
        public InputStream downloadFile(String bucketName, String originalName, HttpServletResponse response) {
            try {
                MinioClient minioClient = MinioClientConfig.getMinioClient();
                InputStream file = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(originalName).build());
                String filename = new String(originalName.getBytes("ISO8859-1"), StandardCharsets.UTF_8);
                if (StringUtils.isNotBlank(originalName)) {
                    filename = originalName;
                }
                response.setHeader("Content-Disposition", "attachment;filename=" + filename);
                ServletOutputStream servletOutputStream = response.getOutputStream();
                int len;
                byte[] buffer = new byte[1024];
                while ((len = file.read(buffer)) > 0) {
                    servletOutputStream.write(buffer, 0, len);
                }
                servletOutputStream.flush();
                file.close();
                servletOutputStream.close();
                return file;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        /**
         * @param bucketName:
         * @description: 删除桶
         * @date 2022/8/16 14:36
         */
        public void deleteBucketName(String bucketName) {
            try {
                if (StringUtils.isBlank(bucketName)) {
                    return;
                }
                MinioClient minioClient = MinioClientConfig.getMinioClient();
                boolean isExist = MinioClientConfig.bucketExists(bucketName);
                if (isExist) {
                    minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error(e.getMessage());
            }
        }

        /**
         * @param bucketName:
         * @description: 删除桶下面所有文件
         * @date 2022/8/16 14:36
         */
        public void deleteBucketFile(String bucketName) {
            try {
                if (StringUtils.isBlank(bucketName)) {
                    return;
                }
                MinioClient minioClient = MinioClientConfig.getMinioClient();
                boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
                if (isExist) {
                    minioClient.deleteBucketEncryption(DeleteBucketEncryptionArgs.builder().bucket(bucketName).build());
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error(e.getMessage());
            }
        }

        /**
         * 根据文件路径得到预览文件绝对地址
         *
         * @param bucketName
         * @param fileName
         * @return
         */
        public String getPreviewFileUrl(String bucketName, String fileName) {
            try {
                MinioClient minioClient = MinioClientConfig.getMinioClient();
                return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(fileName).build());
            } catch (Exception e) {
                e.printStackTrace();
                return "";
            }
        }
    }

    编写配置文件

    package top.fairy.global.globalfairytoppi4j.config;

    import io.minio.BucketExistsArgs;
    import io.minio.MinioClient;
    import io.minio.messages.Bucket;
    import lombok.SneakyThrows;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.stereotype.Component;

    import javax.annotation.PostConstruct;
    import java.util.List;


    @Slf4j
    @Component
    @Configuration
    public class MinioClientConfig {

        @Autowired
        private StorageProperty storageProperty;

        private static MinioClient minioClient;


        /**
         * @description: 获取minioClient
         * @date 2021/6/22 16:55
         * @return io.minio.MinioClient
         */
        public static MinioClient getMinioClient(){
            return minioClient;
        }

        /**
         * 判断 bucket是否存在
         *
         * @param bucketName:
         *            桶名
         * @return: boolean
         * @date : 2020/8/16 20:53
         */
        @SneakyThrows(Exception.class)
        public static boolean bucketExists(String bucketName) {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        }


        /**
         * 获取全部bucket
         *
         * @param :
         * @return: java.util.List
         * @date : 2020/8/16 23:28
         */
        @SneakyThrows(Exception.class)
        public static List getAllBuckets() {
            return minioClient.listBuckets();
        }

        /**
         * 初始化minio配置
         *
         * @param :
         * @return: void
         * @date : 2020/8/16 20:56
         */
        @PostConstruct
        public void init() {
            try {
                minioClient = MinioClient.builder()
                        .endpoint(storageProperty.getEndpoint())
                        .credentials(storageProperty.getAccessKey(), storageProperty.getSecretKey())
                        .build();
            } catch (Exception e) {
                e.printStackTrace();
                log.error("初始化minio配置异常: 【{}】", e.fillInStackTrace());
            }
        }

    }

    编写业务类

    package top.fairy.global.globalfairytoppi4j.action;


    import cn.hutool.core.collection.CollUtil;
    import cn.hutool.json.JSONUtil;
    import com.alibaba.fastjson.JSONObject;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import io.minio.*;
    import io.minio.errors.*;
    import io.minio.messages.Bucket;
    import io.minio.messages.Item;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;
    import top.fairy.global.globalfairytoppi4j.api.CommonResult;
    import top.fairy.global.globalfairytoppi4j.beans.BucketPolicyConfigDto;
    import top.fairy.global.globalfairytoppi4j.beans.FileVo;
    import top.fairy.global.globalfairytoppi4j.beans.MinioUploadDto;
    import top.fairy.global.globalfairytoppi4j.beans.PageUtil;

    import java.io.IOException;
    import java.security.InvalidKeyException;
    import java.security.NoSuchAlgorithmException;
    import java.text.DecimalFormat;
    import java.text.SimpleDateFormat;
    import java.time.LocalDateTime;
    import java.time.ZoneId;
    import java.util.ArrayList;
    import java.util.Comparator;
    import java.util.Date;
    import java.util.List;

    /**
     * MinIO对象存储管理
     * Created by macro on 2019/12/25.
     */
    //@Api(tags = "MinioController", description = "MinIO对象存储管理")
    @Controller
    @RequestMapping("/minio")
    public class MinioController {

        private static final Logger LOGGER = LoggerFactory.getLogger(MinioController.class);
        @Value("${minio.endpoint}")
        private String ENDPOINT;
        @Value("${minio.bucketName}")
        private String BUCKET_NAME;
        @Value("${minio.bucketImg}")
        private String BUCKET_IMG;
        @Value("${minio.bucketVideo}")
        private String BUCKET_VIDEO;
        @Value("${minio.accessKey}")
        private String ACCESS_KEY;
        @Value("${minio.secretKey}")
        private String SECRET_KEY;

    //    @ApiOperation("文件上传")
        @RequestMapping(value = "/upload", method = RequestMethod.POST)
        @ResponseBody
        public CommonResult upload(@RequestPart("file") MultipartFile file) {
            try {
                //创建一个MinIO的Java客户端
                MinioClient minioClient =MinioClient.builder()
                        .endpoint(ENDPOINT)
                        .credentials(ACCESS_KEY,SECRET_KEY)
                        .build();
                boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(BUCKET_NAME).build());
                if (isExist) {
                    LOGGER.info("存储桶已经存在!");
                } else {
                    //创建存储桶并设置只读权限
                    minioClient.makeBucket(MakeBucketArgs.builder().bucket(BUCKET_NAME).build());
                    BucketPolicyConfigDto bucketPolicyConfigDto = createBucketPolicyConfigDto(BUCKET_NAME);
                    SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs.builder()
                            .bucket(BUCKET_NAME)
                            .config(JSONUtil.toJsonStr(bucketPolicyConfigDto))
                            .build();
                    minioClient.setBucketPolicy(setBucketPolicyArgs);
                }
                String filename = file.getOriginalFilename();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
                // 设置存储对象名称
                String objectName = sdf.format(new Date()) + "/" + filename;
                // 使用putObject上传一个文件到存储桶中
                PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                        .bucket(BUCKET_NAME)
                        .object(objectName)
                        .contentType(file.getContentType())
                        .stream(file.getInputStream(), file.getSize(), ObjectWriteArgs.MIN_MULTIPART_SIZE).build();
                minioClient.putObject(putObjectArgs);
                LOGGER.info("文件上传成功!");
                MinioUploadDto minioUploadDto = new MinioUploadDto();
                minioUploadDto.setName(filename);
                minioUploadDto.setUrl(ENDPOINT + "/" + BUCKET_NAME + "/" + objectName);
                return CommonResult.success(minioUploadDto);
            } catch (Exception e) {
                e.printStackTrace();
                LOGGER.info("上传发生错误: {}!", e.getMessage());
            }
            return CommonResult.failed();
        }

        private BucketPolicyConfigDto createBucketPolicyConfigDto(String bucketName) {
            BucketPolicyConfigDto.Statement statement = BucketPolicyConfigDto.Statement.builder()
                    .Effect("Allow")
                    .Principal("*")
                    .Action("s3:GetObject")
                    .Resource("arn:aws:s3:::"+bucketName+"/*.**").build();
            return BucketPolicyConfigDto.builder()
                    .Version("2012-10-17")
                    .Statement(CollUtil.toList(statement))
                    .build();
        }

        /**
         * 获取文件列表
         *
         * @param pageNum  页码
         * @param pageSize 一页的数量
         * @return
         * @throws Exception
         */
        @ResponseBody
        @RequestMapping(value = "/fileList", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
        public String getFileList(Integer pageNum, Integer pageSize) throws Exception {
            String bucketName = "img";
            //创建一个MinIO的Java客户端
            MinioClient minioClient =MinioClient.builder()
                    .endpoint(ENDPOINT)
                    .credentials(ACCESS_KEY,SECRET_KEY)
                    .build();
            DecimalFormat df = new DecimalFormat("0.00");
            List buckets = minioClient.listBuckets();
            List list = new ArrayList<>(32);
            if (!buckets.isEmpty()) {
                buckets.forEach(s -> {
                    try {
                        // 得到bucket下的文件
                        Iterable> results = minioClient.listObjects(s.name());

                        // 循环遍历获取每一个文件对象

                        results.forEach(g -> {
                            try {
                                FileVo fileVo = new FileVo();
                                fileVo.setBucketName(s.name());  // 文件夹名称
                                fileVo.setFileName(g.get().objectName());  // 文件名称
                                fileVo.setUpdateTime(localDateTime2Date(g.get().lastModified().toLocalDateTime()));  // 文件上传时间
                                Long size = g.get().size();
                                if (size > (1024 * 1024)) {
                                    fileVo.setFileSize(df.format(((double) size / 1024 / 1024)) + "MB");  // 文件大小,如果超过1M,则把单位换成MB
                                } else if (size > 1024) {
                                    fileVo.setFileSize(df.format(((double) size / 1024)) + "KB"); // 文件大小,如果没超过1M但是超过1000字节,则把单位换成KB
                                } else {
                                    fileVo.setFileSize( size + "bytes");  // // 文件大小,如果没超过1000字节,则把单位换成bytes
                                }
                                list.add(fileVo);
                            } catch (ErrorResponseException e) {
                                e.printStackTrace();
                            } catch (InsufficientDataException e) {
                                e.printStackTrace();
                            } catch (InternalException e) {
                                e.printStackTrace();
                            } catch (InvalidBucketNameException e) {
                                e.printStackTrace();
                            } catch (InvalidKeyException e) {
                                e.printStackTrace();
                            } catch (InvalidResponseException e) {
                                e.printStackTrace();
                            } catch (IOException e) {
                                e.printStackTrace();
                            } catch (NoSuchAlgorithmException e) {
                                e.printStackTrace();
                            } catch (XmlParserException e) {
                                e.printStackTrace();
                            } catch (ServerException e) {
                                e.printStackTrace();
                            }
                        });
                    } catch (XmlParserException e) {
                        e.printStackTrace();
                    }
                });
            }
            JSONObject res = new JSONObject();
            res.put("code", 200);
            res.put("message", "获取文件列表成功");
            // 按最后上传时间排序
            list.sort(new Comparator() {
                @Override
                public int compare(FileVo o1, FileVo o2) {
                    return o2.getUpdateTime().compareTo(o1.getUpdateTime());
                }
            });
            // 分页
            List returnList = PageUtil.startPage(list, pageNum, pageSize);
            res.put("list", returnList);
            ObjectMapper mapper = new ObjectMapper();
            String s = mapper.writeValueAsString(res);
            return s;
        }

        private Date localDateTime2Date(LocalDateTime toLocalDateTime) {
            Date date = Date.from( toLocalDateTime.atZone( ZoneId.systemDefault()).toInstant());
            return date;
        }

        //    @ApiOperation("文件删除")文件删除
        @RequestMapping(value = "/delete", method = RequestMethod.POST)
        @ResponseBody
        public CommonResult delete(@RequestParam("objectName") String objectName) {
            try {
                MinioClient minioClient = MinioClient.builder()
                        .endpoint(ENDPOINT)
                        .credentials(ACCESS_KEY,SECRET_KEY)
                        .build();
                minioClient.removeObject(RemoveObjectArgs.builder().bucket(BUCKET_NAME).object(objectName).build());
                return CommonResult.success(null);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return CommonResult.failed();
        }
    }

    业务类service,dao入库等操作请自行补充。

    源码下载地址:

    global-fairy-top-pi4j: java语言读取dth11温湿度传感器参数,通过接口对外提供

    具体版本参考:v0.3.1

    1. 打包部署

      Idea中maven package项目之后,拷贝jar包到云服务器

    1. 启动

      nohup java -jar global-fairy-top-pi4j-0.0.1-SNAPSHOT.jar &

    1. 测试:

      localhost:8082/minio/fileList

    • 注意事项
    1. 常见问题

     部署demo前请先安装mysql,并配置好地址。

        

  • 相关阅读:
    Day15: C++之STL容器(3/3)
    Spring AI 第二讲 之 Chat Model API 第三节Azure OpenAI Chat
    CVF 在 TNEWS 数据集上测试
    第四讲 vue生命周期
    某金融机构分布式数据库架构方案与运维方案设计分享
    Java8时间日期库DateTime API及示例
    UI设计需要学会哪些软件?优漫动游
    ConfigurableBeanFactory 源码分析
    rabbitMQ的生产与消费由Kettle实现
    人工神经网络的应用价值,人工智能神经网络应用
  • 原文地址:https://blog.csdn.net/jiao_zg/article/details/132693869