MINIO介绍
什么是对象存储?
以阿里云OSS为例:
| 对象存储服务OSS(Object Storage Service)是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。 |
| 优势就在于它可以存储大容量的非结构化数据。 |
| 缺点:没有选择业务上云或者想要下云的企业,使用公有云的 OSS,在公网带宽方面就需要有一定的投入,毕竟需要通过公网传输,带宽太小,传输速度就会慢,且在传输过程中数据的安全性和完整性也有损失的风险,走专线的费用又十分昂贵,不实在。 |
| 此时MinIO 就是一个不错的选择,麻雀虽小,五脏俱全,企业可以以此快速构建自己内部的对象存储服务。 |
什么是minio?
| Minio 是个基于 Golang 编写的开源对象存储套件,基于Apache License v2.0开源协议,虽然轻量,却拥有着不错的性能。 |
| 可以很简单的和NodeJS、Redis、MySQL等结合使用。 |
minio结构:
| 由桶(bucket,对应Windows下的文件夹),组成目录结构,桶中直接存放对象(Object,对应Windwos下的文件),桶中不能再创建桶,但是能创建文件夹 。 |
minio特性:
| 高性能、可扩展、云原生、图形化界面、支持纠删码。除了Minio自己的文件系统,还支持 DAS、 JBODs、NAS、Google云存储和 Azure Blob存储。Minio服务器通过其兼容AWS SNS / SQS的事件通知服务触发Lambda功能。支持的目标是消息队列,如Kafka,NATS,AMQP,MQTT,Webhooks以及Elasticsearch,Redis,Postgres和MySQL等数据库。 |
minio使用:
1.下载:
| 1.在home目录下创建minio文件夹 |
| mkdir /home/minio |
| 2、进入/home/minio 文件夹 |
| cd /home/minio |
| 3、下载文件 |
| wget https://dl.min.io/server/minio/release/linux-amd64/minio |
| 注意:如果上一行报错,请先运行yun install wget |
| 4.创建数据文件夹 |
| mkdir /home/minio/data |
| mkdir /home/minio/log |
| 5.创建日志文件 |
| touch /home/minio/log/minio.log |
| 6.启动 |
| 6.1赋予权限 |
| chmod 777 minio |
| 6.2.1前台启动命令 |
| ./minio server /home/minio/data |
| 6.2.2后台启动命令 |
| nohup ./minio server /home/minio/data > /home/minio/log/minio.log & |
| 6.2.3后台指定端口号启动(以9001为例) |
| nohup ./minio server --console-address ":9001" /home/minio/data > /home/minio/log/minio.log 2>&1 & |
| 7.修改超管账户名和密码(为了安全) |
| 7.1打开 /etc/profile 文件 |
| vim /etc/profile |
| 7.2在文件的最末尾加上以下信息 |
| 注意看提示,新版本需要用MINIO_ROOT_USER和MINIO_ROOT_PASSWORD, |
| 旧版需要用MINIO_ACCESS_KEY和MINIO_SECRET_KEY |
| 按 i 键后,在文档末尾输入 |
| (新版) |
| export MINIO_ROOT_USER=minioadmin |
| export MINIO_ROOT_PASSWORD=你想改的密码 |
| (旧版) |
| export MINIO_ACCESS_KEY=minioadmin |
| export MINIO_SECRET_KEY=你想改的密码 |
| 注:这里如果你修改的配置密码不生效,记得重载环境变量配置文件,并重启minio服务(用ps找到然后kill掉重新启动) |
| 8.保存退出esc :wq |
| 9.重载配置 |
| source /etc/profile |
| 10.打开127.0.0.1:9000 即可看到运行页面 |

| 1.通过命令查看端口 |
| ps -aux | grep minio |
| 2.kill杀死进程 |
| kill -9 进程号(五位数字) |
| 3.开放下载,设置永久链接 |
| 3.1下载客户端 |
| wget https://dl.minio.io/client/mc/release/linux-amd64/mc |
| 3.2赋予权限 |
| chomd 777mc |
| 3.3添加server |
| ./mc config host add minio htt//你的ip:9000/ minioadmin 你的minio密码 |
| 3.4设置需要开放下载的bucket |
| ./mc anonymous set download minio/dev |
| 4.文件访问地址: |
| http://你的ip:9000/dev/年/月/日/文件名 |
minio在springboot中使用
1.导入依赖 刷新maven
| <dependency> |
| <groupId>io.minio</groupId> |
| <artifactId>minio</artifactId> |
| <version>7.0.2</version> |
| </dependency> |
| |
2.在application.yml中新增配置
| |
| minio: |
| endpoint: 127.0.0.1 |
| port: 9000 |
| accessKey: minioadmin |
| secretKey: minioadmin |
| secure: false |
| bucketName: "guoba" |
| configDir: "/home/guoba" |
| |
3.java代码接口
| |
| |
| |
| |
| |
| @Override |
| public AjaxResult upload(MultipartFile file) { |
| InputStream inputStream = null; |
| |
| MinioClient minioClient = getClient(); |
| |
| String bucketName = minioConfig.getBucketName(); |
| try { |
| inputStream = file.getInputStream(); |
| |
| boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().build()); |
| if (exists) { |
| System.out.println("该桶已经存在"); |
| } else { |
| minioClient.makeBucket(MakeBucketArgs.builder().build()); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| String fileName = file.getOriginalFilename(); |
| String objectName = new SimpleDateFormat("yyyy/MM/dd/").format(new Date()) + UUID.randomUUID().toString().replaceAll("-", "") |
| + fileName.substring(fileName.lastIndexOf(".")); |
| PutObjectArgs objectArgs = PutObjectArgs.builder().object(objectName) |
| .bucket(bucketName) |
| .contentType(file.getContentType()) |
| .stream(file.getInputStream(), file.getSize(), -1).build(); |
| minioClient.putObject(objectArgs); |
| |
| AjaxResult ajax = AjaxResult.success(); |
| ajax.put("fileName", "/" + bucketName + "/" + objectName); |
| |
| ajax.put("url", minioConfig.getEndpoint() + ":" + minioConfig.getPort() + "/" + minioConfig.getBucketName() + "/" + fileName); |
| |
| |
| |
| |
| |
| |
| |
| return ajax; |
| } catch (Exception e) { |
| e.printStackTrace(); |
| return AjaxResult.error("上传失败"); |
| } finally { |
| |
| if (inputStream != null) { |
| try { |
| inputStream.close(); |
| } catch (IOException e) { |
| log.debug("inputStream close IOException:" + e.getMessage()); |
| } |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| private MinioClient getClient() { |
| MinioClient minioClient = |
| MinioClient.builder() |
| .endpoint("http://" + minioConfig.getEndpoint() + ":" + minioConfig.getPort()) |
| .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey()) |
| .build(); |
| return minioClient; |
| } |
| |
| |
若依Java接口
| |
| |
| |
| @PostMapping("/uploadMinio") |
| public AjaxResult uploadFileMinio(MultipartFile file) throws Exception |
| { |
| try |
| { |
| |
| String fileName = FileUploadUtils.uploadMinio(file); |
| AjaxResult ajax = AjaxResult.success(); |
| ajax.put("url", fileName); |
| ajax.put("fileName", fileName); |
| ajax.put("newFileName", FileUtils.getName(fileName)); |
| ajax.put("originalFilename", file.getOriginalFilename()); |
| return ajax; |
| } |
| catch (Exception e) |
| { |
| return AjaxResult.error(e.getMessage()); |
| } |
| } |
| |
| |
文件上传工具类
| package com.ruoyi.common.utils.file; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.file.Paths; |
| import java.util.Objects; |
| import org.apache.commons.io.FilenameUtils; |
| import org.springframework.web.multipart.MultipartFile; |
| import com.ruoyi.common.config.MinioConfig; |
| import com.ruoyi.common.config.RuoYiConfig; |
| import com.ruoyi.common.constant.Constants; |
| import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException; |
| import com.ruoyi.common.exception.file.FileSizeLimitExceededException; |
| import com.ruoyi.common.exception.file.InvalidExtensionException; |
| import com.ruoyi.common.utils.DateUtils; |
| import com.ruoyi.common.utils.StringUtils; |
| import com.ruoyi.common.utils.uuid.Seq; |
| |
| |
| |
| |
| |
| |
| public class FileUploadUtils |
| { |
| |
| |
| |
| public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; |
| |
| |
| |
| |
| public static final int DEFAULT_FILE_NAME_LENGTH = 100; |
| |
| |
| |
| |
| private static String defaultBaseDir = RuoYiConfig.getProfile(); |
| |
| |
| |
| |
| private static String bucketName = MinioConfig.getBucketName(); |
| |
| public static void setDefaultBaseDir(String defaultBaseDir) |
| { |
| FileUploadUtils.defaultBaseDir = defaultBaseDir; |
| } |
| |
| public static String getDefaultBaseDir() |
| { |
| return defaultBaseDir; |
| } |
| |
| public static String getBucketName() |
| { |
| return bucketName; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| public static final String upload(MultipartFile file) throws IOException |
| { |
| try |
| { |
| return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); |
| } |
| catch (Exception e) |
| { |
| throw new IOException(e.getMessage(), e); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static final String upload(String baseDir, MultipartFile file) throws IOException |
| { |
| try |
| { |
| return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); |
| } |
| catch (Exception e) |
| { |
| throw new IOException(e.getMessage(), e); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) |
| throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, |
| InvalidExtensionException |
| { |
| int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); |
| if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) |
| { |
| throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); |
| } |
| |
| assertAllowed(file, allowedExtension); |
| |
| String fileName = extractFilename(file); |
| |
| String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); |
| file.transferTo(Paths.get(absPath)); |
| return getPathFileName(baseDir, fileName); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| public static final String uploadMinio(MultipartFile file) throws IOException |
| { |
| try |
| { |
| return uploadMinino(getBucketName(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); |
| } |
| catch (Exception e) |
| { |
| throw new IOException(e.getMessage(), e); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| public static final String uploadMinio(MultipartFile file, String bucketName) throws IOException |
| { |
| try |
| { |
| return uploadMinino(bucketName, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); |
| } |
| catch (Exception e) |
| { |
| throw new IOException(e.getMessage(), e); |
| } |
| } |
| |
| private static final String uploadMinino(String bucketName, MultipartFile file, String[] allowedExtension) |
| throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, |
| InvalidExtensionException |
| { |
| int fileNamelength = file.getOriginalFilename().length(); |
| if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) |
| { |
| throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); |
| } |
| assertAllowed(file, allowedExtension); |
| try |
| { |
| String fileName = extractFilename(file); |
| String pathFileName = MinioUtil.uploadFile(bucketName, fileName, file); |
| return pathFileName; |
| } |
| catch (Exception e) |
| { |
| throw new IOException(e.getMessage(), e); |
| } |
| } |
| |
| |
| |
| |
| public static final String extractFilename(MultipartFile file) |
| { |
| return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), |
| FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); |
| } |
| |
| public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException |
| { |
| File desc = new File(uploadDir + File.separator + fileName); |
| |
| if (!desc.exists()) |
| { |
| if (!desc.getParentFile().exists()) |
| { |
| desc.getParentFile().mkdirs(); |
| } |
| } |
| return desc; |
| } |
| |
| public static final String getPathFileName(String uploadDir, String fileName) throws IOException |
| { |
| int dirLastIndex = RuoYiConfig.getProfile().length() + 1; |
| String currentDir = StringUtils.substring(uploadDir, dirLastIndex); |
| return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static final void assertAllowed(MultipartFile file, String[] allowedExtension) |
| throws FileSizeLimitExceededException, InvalidExtensionException |
| { |
| long size = file.getSize(); |
| if (size > DEFAULT_MAX_SIZE) |
| { |
| throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); |
| } |
| |
| String fileName = file.getOriginalFilename(); |
| String extension = getExtension(file); |
| if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) |
| { |
| if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) |
| { |
| throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, |
| fileName); |
| } |
| else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) |
| { |
| throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, |
| fileName); |
| } |
| else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) |
| { |
| throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, |
| fileName); |
| } |
| else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) |
| { |
| throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, |
| fileName); |
| } |
| else |
| { |
| throw new InvalidExtensionException(allowedExtension, extension, fileName); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| public static final boolean isAllowedExtension(String extension, String[] allowedExtension) |
| { |
| for (String str : allowedExtension) |
| { |
| if (str.equalsIgnoreCase(extension)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static final String getExtension(MultipartFile file) |
| { |
| String extension = FilenameUtils.getExtension(file.getOriginalFilename()); |
| if (StringUtils.isEmpty(extension)) |
| { |
| extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); |
| } |
| return extension; |
| } |
| } |
MinioConfig.java
| package com.ruoyi.common.config; |
| |
| import org.springframework.boot.context.properties.ConfigurationProperties; |
| import org.springframework.context.annotation.Bean; |
| import org.springframework.context.annotation.Configuration; |
| import io.minio.MinioClient; |
| |
| |
| |
| |
| |
| |
| @Configuration |
| @ConfigurationProperties(prefix = "minio") |
| public class MinioConfig |
| { |
| |
| |
| |
| private static String url; |
| |
| |
| |
| |
| private static String accessKey; |
| |
| |
| |
| |
| private static String secretKey; |
| |
| |
| |
| |
| private static String bucketName; |
| |
| public static String getUrl() |
| { |
| return url; |
| } |
| |
| public void setUrl(String url) |
| { |
| MinioConfig.url = url; |
| } |
| |
| public static String getAccessKey() |
| { |
| return accessKey; |
| } |
| |
| public void setAccessKey(String accessKey) |
| { |
| MinioConfig.accessKey = accessKey; |
| } |
| |
| public static String getSecretKey() |
| { |
| return secretKey; |
| } |
| |
| public void setSecretKey(String secretKey) |
| { |
| MinioConfig.secretKey = secretKey; |
| } |
| |
| public static String getBucketName() |
| { |
| return bucketName; |
| } |
| |
| public void setBucketName(String bucketName) |
| { |
| MinioConfig.bucketName = bucketName; |
| } |
| |
| @Bean |
| public MinioClient getMinioClient() |
| { |
| return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build(); |
| } |
| } |
| |
MinioUtil.java
| package com.ruoyi.common.utils.file; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import org.springframework.web.multipart.MultipartFile; |
| import com.ruoyi.common.utils.ServletUtils; |
| import com.ruoyi.common.utils.spring.SpringUtils; |
| import io.minio.GetPresignedObjectUrlArgs; |
| import io.minio.MinioClient; |
| import io.minio.PutObjectArgs; |
| import io.minio.http.Method; |
| |
| |
| |
| |
| |
| |
| public class MinioUtil |
| { |
| |
| |
| |
| |
| |
| |
| |
| public static String uploadFile(String bucketName, String fileName, MultipartFile multipartFile) throws IOException |
| { |
| String url = ""; |
| MinioClient minioClient = SpringUtils.getBean(MinioClient.class); |
| try (InputStream inputStream = multipartFile.getInputStream()) |
| { |
| minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(inputStream, multipartFile.getSize(), -1).contentType(multipartFile.getContentType()).build()); |
| url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(fileName).method(Method.GET).build()); |
| url = url.substring(0, url.indexOf('?')); |
| return ServletUtils.urlDecode(url); |
| } |
| catch (Exception e) |
| { |
| throw new IOException(e.getMessage(), e); |
| } |
| } |
| } |
| |