> 文档中心 > springboot整合minio对象存储服务

springboot整合minio对象存储服务

目录

  • 一、理解
  • 二、下载
  • 三、整合springboot
    • 1、配置
    • 2、工具类
    • 3、测试

一、理解

MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。

MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。
支持分布式上传的数据是在自己的云服务器上

二、下载

其他下载方式可以看官网http://docs.minio.org.cn/docs/

docker下载安装

# 下载镜像docker pull minio/minio#运行容器docker run -d -p 9000:9000 -p 9090:9090 --name=minio --restart=always -e "MINIO_ROOT_USER=admin" -e "MINIO_ROOT_PASSWORD=admin123456" -v /home/minio/data:/data -v /home/minio/config:/root/.minio  minio/minio server /data --console-address ":9000" --address ":9090"

1、不加–console-address “:9000” --address ":9090"会导致端口一直变,页面访问不了
2、密码不低于8位
访问 http//:ip:9000 账号密码是你设置的

在这里插入图片描述新建个桶,文件存在桶里
在这里插入图片描述

docker logs 容器id 查看日志
代码api连接的地址是ip:9090
springboot整合minio对象存储服务

三、整合springboot

1、配置

pom依赖

  <dependency>      <groupId>io.minio</groupId>      <artifactId>minio</artifactId>      <version>8.2.1</version>  </dependency>

yml配置

server:  port: 7777  # minio文件上传minio:  minio_url: 你的ip:9090  #api的端口号  minio_name: admin  minio_pass: admin123456  bucketName: test

新建配置类

@Configurationpublic class MinIoClientConfig {@Value(value = "${minio.minio_url}")private String minioUrl;@Value(value = "${minio.minio_name}")private String minioName;@Value(value = "${minio.minio_pass}")private String minioPass;@Value(value = "${minio.bucketName}")private String bucketName;@Beanpublic void initMinio(){if(!minioUrl.startsWith("http")){minioUrl = "http://" + minioUrl;}if(!minioUrl.endsWith("/")){minioUrl = minioUrl.concat("/");}// 直接将MinioUtil类的set进去,MinioUtil就可以直接写成静态方法MinioUtil.setMinioUrl(minioUrl);MinioUtil.setMinioName(minioName);MinioUtil.setMinioPass(minioPass);MinioUtil.setBucketName(bucketName);}

2、工具类

文件过滤类(给minio上传用)
判断类型等,主要做一些限制

package com.example.minio.util;import org.springframework.web.multipart.MultipartFile;import org.apache.commons.lang3.StringUtils;import java.io.InputStream;import java.util.HashMap;import java.util.Iterator;import java.util.regex.Matcher;import java.util.regex.Pattern;import java.util.regex.PatternSyntaxException;/** * @Description: TODO * @author: lsq * @date: 2021年08月09日 15:29 */public class FileFilterUtil {    //文件后缀    private static String[] forbidType = {"jsp","php"};    // 初始化文件头类型,不够的自行补充    final static HashMap<String, String> fileTypeMap = new HashMap<>();    static { fileTypeMap.put("3c25402070616765206c", "jsp"); fileTypeMap.put("3c3f7068700a0a2f2a2a0a202a205048", "php");/* fileTypeMap.put("ffd8ffe000104a464946", "jpg"); fileTypeMap.put("89504e470d0a1a0a0000", "png"); fileTypeMap.put("47494638396126026f01", "gif"); fileTypeMap.put("49492a00227105008037", "tif"); fileTypeMap.put("424d228c010000000000", "bmp"); fileTypeMap.put("424d8240090000000000", "bmp"); fileTypeMap.put("424d8e1b030000000000", "bmp"); fileTypeMap.put("41433130313500000000", "dwg"); fileTypeMap.put("3c21444f435459504520", "html"); fileTypeMap.put("3c21646f637479706520", "htm"); fileTypeMap.put("48544d4c207b0d0a0942", "css"); fileTypeMap.put("696b2e71623d696b2e71", "js"); fileTypeMap.put("7b5c727466315c616e73", "rtf"); fileTypeMap.put("38425053000100000000", "psd"); fileTypeMap.put("46726f6d3a203d3f6762", "eml"); fileTypeMap.put("d0cf11e0a1b11ae10000", "doc"); fileTypeMap.put("5374616E64617264204A", "mdb"); fileTypeMap.put("252150532D41646F6265", "ps"); fileTypeMap.put("255044462d312e350d0a", "pdf"); fileTypeMap.put("2e524d46000000120001", "rmvb"); fileTypeMap.put("464c5601050000000900", "flv"); fileTypeMap.put("00000020667479706d70", "mp4"); fileTypeMap.put("49443303000000002176", "mp3"); fileTypeMap.put("000001ba210001000180", "mpg"); fileTypeMap.put("3026b2758e66cf11a6d9", "wmv"); fileTypeMap.put("52494646e27807005741", "wav"); fileTypeMap.put("52494646d07d60074156", "avi"); fileTypeMap.put("4d546864000000060001", "mid"); fileTypeMap.put("504b0304140000000800", "zip"); fileTypeMap.put("526172211a0700cf9073", "rar"); fileTypeMap.put("235468697320636f6e66", "ini"); fileTypeMap.put("504b03040a0000000000", "jar"); fileTypeMap.put("4d5a9000030000000400", "exe"); fileTypeMap.put("3c25402070616765206c", "jsp"); fileTypeMap.put("4d616e69666573742d56", "mf"); fileTypeMap.put("3c3f786d6c2076657273", "xml"); fileTypeMap.put("494e5345525420494e54", "sql"); fileTypeMap.put("7061636b616765207765", "java"); fileTypeMap.put("406563686f206f66660d", "bat"); fileTypeMap.put("1f8b0800000000000000", "gz"); fileTypeMap.put("6c6f67346a2e726f6f74", "properties"); fileTypeMap.put("cafebabe0000002e0041", "class"); fileTypeMap.put("49545346030000006000", "chm"); fileTypeMap.put("04000000010000001300", "mxp"); fileTypeMap.put("504b0304140006000800", "docx"); fileTypeMap.put("6431303a637265617465", "torrent"); fileTypeMap.put("6D6F6F76", "mov"); fileTypeMap.put("FF575043", "wpd"); fileTypeMap.put("CFAD12FEC5FD746F", "dbx"); fileTypeMap.put("2142444E", "pst"); fileTypeMap.put("AC9EBD8F", "qdf"); fileTypeMap.put("E3828596", "pwl"); fileTypeMap.put("2E7261FD", "ram");*/    }    /**     * @param fileName     * @return String     * @description 通过文件后缀名获取文件类型     */    private static String getFileTypeBySuffix(String fileName) { return fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());    }    /**     * 文件类型过滤     *     * @param file     */    public static void fileTypeFilter(MultipartFile file) throws Exception { String suffix = getFileType(file); for (String type : forbidType) {     if (type.contains(suffix)) {  throw new Exception("上传失败,文件类型异常:" + suffix);     } }    }    /**     * 通过读取文件头部获得文件类型     *     * @param file     * @return 文件类型     * @throws Exception     */    private static String getFileType(MultipartFile file) throws Exception { String fileExtendName = null; InputStream is; try {     //is = new FileInputStream(file);     is = file.getInputStream();     byte[] b = new byte[10];     is.read(b, 0, b.length);     String fileTypeHex = String.valueOf(bytesToHexString(b));     Iterator<String> keyIter = fileTypeMap.keySet().iterator();     while (keyIter.hasNext()) {  String key = keyIter.next();  // 验证前5个字符比较  if (key.toLowerCase().startsWith(fileTypeHex.toLowerCase().substring(0, 5))   || fileTypeHex.toLowerCase().substring(0, 5).startsWith(key.toLowerCase())) {      fileExtendName = fileTypeMap.get(key);      break;  }     }     // 如果不是上述类型,则判断扩展名     if (StringUtils.isBlank(fileExtendName)) {  String fileName = file.getOriginalFilename();  return getFileTypeBySuffix(fileName);     }     is.close();     return fileExtendName; } catch (Exception exception) {     throw new Exception(exception.getMessage(), exception); }    }    /**     * 获得文件头部字符串     *     * @param src     * @return     */    private static String bytesToHexString(byte[] src) { StringBuilder stringBuilder = new StringBuilder(); if (src == null || src.length <= 0) {     return null; } for (int i = 0; i < src.length; i++) {     int v = src[i] & 0xFF;     String hv = Integer.toHexString(v);     if (hv.length() < 2) {  stringBuilder.append(0);     }     stringBuilder.append(hv); } return stringBuilder.toString();    }    /**     *  文件上传字符串过滤特殊字符     * @param str     * @return     * @throws PatternSyntaxException     */    public static String filter(String str) throws PatternSyntaxException { // 清除掉所有特殊字符 String regEx = "[`_《》~!@#$%^&*()+=|{}':;',\\[\\].?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]"; Pattern p = Pattern.compile(regEx); Matcher m = p.matcher(str); return m.replaceAll("").trim();    }    /**     * 判断文件名是否带盘符,重新处理     * @param fileName     * @return     */    public static String getFileName(String fileName){ //判断是否带有盘符信息 // Check for Unix-style path int unixSep = fileName.lastIndexOf('/'); // Check for Windows-style path int winSep = fileName.lastIndexOf('\\'); // Cut off at latest possible point int pos = (winSep > unixSep ? winSep : unixSep); if (pos != -1)  {     // Any sort of path separator found...     fileName = fileName.substring(pos + 1); } //替换上传文件名字的特殊字符 fileName = fileName.replace("=","").replace(",","").replace("&","")  .replace("#", "").replace("“", "").replace("”", ""); //替换上传文件名字中的空格 fileName=fileName.replaceAll("\\s",""); return fileName;    }}

MInio工具类
静态工具类

package com.example.minio.util;import io.minio.*;import io.minio.http.Method;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.ObjectUtils;import org.springframework.web.multipart.MultipartFile;import java.io.InputStream;import java.net.URLDecoder;@Slf4jpublic class MinioUtil {private static String minioUrl;private static String minioName;private static String minioPass;private static String bucketName;public static void setMinioUrl(String minioUrl) {MinioUtil.minioUrl = minioUrl;}public static void setMinioName(String minioName) {MinioUtil.minioName = minioName;}public static void setMinioPass(String minioPass) {MinioUtil.minioPass = minioPass;}public static void setBucketName(String bucketName) {MinioUtil.bucketName = bucketName;}public static String getMinioUrl() {return minioUrl;}public static String getBucketName() {return bucketName;}private static MinioClient minioClient = null;/** * 上传文件 * @param file * @param bizPath 上传路径 * @param customBucket 桶名 * @return */public static String upload(MultipartFile file, String bizPath, String customBucket) {String file_url = "";//update-begin-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击bizPath= FileFilterUtil.filter(bizPath);//update-end-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击String newBucket = bucketName;if(ObjectUtils.isNotEmpty(customBucket)){newBucket = customBucket;}try {initMinio(minioUrl, minioName,minioPass);// 检查存储桶是否已经存在if(minioClient.bucketExists(BucketExistsArgs.builder().bucket(newBucket).build())) {log.info("Bucket already exists.");} else {// 创建一个名为ota的存储桶minioClient.makeBucket(MakeBucketArgs.builder().bucket(newBucket).build());log.info("create a new bucket.");}//update-begin-author:liusq date:20210809 for: 过滤上传文件类型FileFilterUtil.fileTypeFilter(file);//update-end-author:liusq date:20210809 for: 过滤上传文件类型InputStream stream = file.getInputStream();// 获取文件名String orgName = file.getOriginalFilename();if("".equals(orgName)){orgName=file.getName();}orgName = FileFilterUtil.getFileName(orgName);String objectName = bizPath+"/"+( orgName.indexOf(".")==-1?orgName + "_" + System.currentTimeMillis():orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf(".")));// 使用putObject上传一个本地文件到存储桶中。if(objectName.startsWith("/")){objectName = objectName.substring(1);}PutObjectArgs objectArgs = PutObjectArgs.builder().object(objectName).bucket(newBucket).contentType("application/octet-stream").stream(stream,stream.available(),-1).build();minioClient.putObject(objectArgs);stream.close();file_url = minioUrl+newBucket+"/"+objectName;}catch (Exception e){log.error(e.getMessage(), e);}return file_url;}/** * 文件上传 * @param file * @param bizPath * @return */public static String upload(MultipartFile file, String bizPath) {return  upload(file,bizPath,null);}/** * 获取文件流 * @param bucketName 桶名 * @param objectName 路径文件名(写全 如图片在image文件夹下 image/xxx.png) * @return */public static InputStream getMinioFile(String bucketName,String objectName){InputStream inputStream = null;try {initMinio(minioUrl, minioName, minioPass);GetObjectArgs objectArgs = GetObjectArgs.builder().object(objectName).bucket(bucketName).build();inputStream = minioClient.getObject(objectArgs);} catch (Exception e) {log.info("文件获取失败" + e.getMessage());}return inputStream;}/** * 删除文件 * @param bucketName * @param objectName 路径文件名(写全 如图片在image文件夹下 image/xxx.png) * @throws Exception */public static void removeObject(String bucketName, String objectName) {try {initMinio(minioUrl, minioName,minioPass);RemoveObjectArgs objectArgs = RemoveObjectArgs.builder().object(objectName).bucket(bucketName).build();minioClient.removeObject(objectArgs);}catch (Exception e){log.info("文件删除失败" + e.getMessage());}}/** * 获取文件外链 * @param bucketName 桶名 * @param objectName 路径文件名(写全 如图片在image文件夹下 image/xxx.png) * @param expires * @return */public static String getObjectURL(String bucketName, String objectName, Integer expires) {initMinio(minioUrl, minioName,minioPass);try{//update-begin---author:liusq  Date:20220121  for:获取文件外链报错提示method不能为空,导致文件下载和预览失败----GetPresignedObjectUrlArgs objectArgs = GetPresignedObjectUrlArgs.builder().object(objectName).bucket(bucketName).expiry(expires).method(Method.GET).build();//update-begin---author:liusq  Date:20220121  for:获取文件外链报错提示method不能为空,导致文件下载和预览失败----String url = minioClient.getPresignedObjectUrl(objectArgs);return URLDecoder.decode(url,"UTF-8");}catch (Exception e){log.info("文件路径获取失败" + e.getMessage());}return null;}/** * 初始化客户端 * @param minioUrl * @param minioName * @param minioPass * @return */private static MinioClient initMinio(String minioUrl, String minioName,String minioPass) {if (minioClient == null) {try {minioClient = MinioClient.builder().endpoint(minioUrl).credentials(minioName, minioPass).build();} catch (Exception e) {e.printStackTrace();}}return minioClient;}/** * 上传文件到minio * @param stream 输入流方式 * @param relativePath * @return */public static String upload(InputStream stream,String relativePath) throws Exception {initMinio(minioUrl, minioName,minioPass);if(minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {log.info("Bucket already exists.");} else {// 创建一个名为ota的存储桶minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());log.info("create a new bucket.");}PutObjectArgs objectArgs = PutObjectArgs.builder().object(relativePath).bucket(bucketName).contentType("application/octet-stream").stream(stream,stream.available(),-1).build();minioClient.putObject(objectArgs);stream.close();return minioUrl+bucketName+"/"+relativePath;}}

在这里插入图片描述

3、测试

@RestControllerpublic class testController {/** * 上传 * @param file */@PostMapping("/upload")public void upload(MultipartFile file){MinioUtil.upload(file,"/image","test");}/** * 下载文件 * @param bucketName 桶名 * @param objectName 路径文件名(写全 如图片在image文件夹下 image/xxx.png) * @param httpResponse * @throws IOException */@GetMapping("/getFile")public void getFile(String bucketName, String objectName, HttpServletResponse httpResponse) throws IOException {InputStream minioFile = MinioUtil.getMinioFile(bucketName, objectName);byte buf[] = new byte[1024];int length = 0;httpResponse.reset();httpResponse.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(objectName, "UTF-8"));httpResponse.setContentType("application/octet-stream");httpResponse.setCharacterEncoding("utf-8");OutputStream outputStream = httpResponse.getOutputStream();while ((length = minioFile.read(buf)) > 0) {outputStream.write(buf, 0, length);}outputStream.close();}/** * 删除文件 * @param bucketName  桶名 * @param objectName 路径文件名(写全 如图片在image文件夹下 image/xxx.png) */@GetMapping("/removeFile")public void removeFile(String bucketName,String objectName){MinioUtil.removeObject(bucketName,objectName);}/** * 获取文件外链 * @param bucketName 桶名 * @param objectName 路径文件名(写全 如图片在image文件夹下 image/xxx.png) */@GetMapping("/getObjectURL")public void getObjectURL(String bucketName,String objectName){String objectURL = MinioUtil.getObjectURL(bucketName, objectName, 100);System.out.println("objectURL = " + objectURL);}}

git地址:https://gitee.com/hmb000/minio.git

推币机的世界