Minio
上传文件如果不使用云服务的话,需要本地搭建,一般选择 FastDFS 但是 FastDFS 安装比较复杂,今天了解一款安装使用更简单的存储系统 MinIO
MinIO 这是一款高性能、分布式的对象存储系统. 它是一款软件产品, 可以100%的运行在标准硬件上。即X86等低成本机器也能够很好的运行MinIO。
MinIO与传统的存储和其他的对象存储不同的是:它一开始就针对性能要求更高的私有云标准进行软件架构设计。因为MinIO一开始就只为对象存储而设计。所以他采用了更易用的方式进行设计,它能实现对象存储所需要的全部功能,在性能上也更加强劲,它不会为了更多的业务功能而妥协,失去MinIO的易用性、高效性。 这样的结果所带来的好处是:它能够更简单地实现具有弹性伸缩能力的原生对象存储服务。
MinIO在传统对象存储用例(例如辅助存储,灾难恢复和归档)方面表现出色。同时,它在机器学习、大数据、私有云、混合云等方面的存储技术上也独树一帜。当然,也不排除数据分析、高性能应用负载、原生云的支持。
在中国:阿里巴巴、腾讯、百度、中国联通、华为、中国移动等等9000多家企业也都在使用MinIO产品
安装 Minio
使用docker安装
拉取镜像
docker pull minio/minio
启动
docker run -p:9000 -p 9001:9001 -d --name minio -v /opt/docker/minio/data:/data -v /opt/docker/minio/config:/root/.minio -e "MINIO_ROOT_USER=minio" -e "MINIO_ROOT_PASSWORD=minio@123456" minio/minio server /data --console-address ":9000" --address ":9001"
使用9000端口 登录控制台
创建存储桶
设置桶权限
创建 Java 客户端
依赖
<dependencies> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-web</artifactId> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-test</artifactId> | |
<scope>test</scope> | |
<exclusions> | |
<exclusion> | |
<groupId>org.junit.vintage</groupId> | |
<artifactId>junit-vintage-engine</artifactId> | |
</exclusion> | |
</exclusions> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-configuration-processor</artifactId> | |
<optional>true</optional> | |
</dependency> | |
<dependency> | |
<groupId>org.projectlombok</groupId> | |
<artifactId>lombok</artifactId> | |
<optional>true</optional> | |
</dependency> | |
<!-- minio依赖 --> | |
<dependency> | |
<groupId>io.minio</groupId> | |
<artifactId>minio</artifactId> | |
<version>.2.1</version> | |
</dependency> | |
<!-- hutool工具类 --> | |
<dependency> | |
<groupId>cn.hutool</groupId> | |
<artifactId>hutool-all</artifactId> | |
<version>.1.2</version> | |
</dependency> | |
<!-- 压缩图片 --> | |
<dependency> | |
<groupId>net.coobird</groupId> | |
<artifactId>thumbnailator</artifactId> | |
<version>.4.8</version> | |
</dependency> | |
<!-- 工具类依赖 --> | |
<dependency> | |
<groupId>org.apache.commons</groupId> | |
<artifactId>commons-lang</artifactId> | |
<version>.12.0</version> | |
</dependency> | |
<dependency> | |
<groupId>com.github.davidcarboni</groupId> | |
<artifactId>encrypted-</artifactId> | |
<version>.0.0</version> | |
</dependency> | |
</dependencies> |
配置文件
spring: | |
application: | |
name: minio-demo | |
servlet: | |
multipart: | |
max-file-size:MB | |
max-request-size:MB | |
server: | |
port: | |
minio: | |
endpoint: http://你的ip: | |
accessKey: minio | |
secretKey: minio@ | |
nginxHost: http://你的域名 |
配置文件配置类
package com.sqm.minio_demo.config; | |
import lombok.Data; | |
import org.springframework.boot.context.properties.ConfigurationProperties; | |
import org.springframework.stereotype.Component; | |
prefix = "minio") | (|
public class MinioProperties { | |
/** | |
* 连接地址 | |
*/ private String endpoint; | |
/** | |
* 用户名 | |
*/ private String accessKey; | |
/** | |
* 密码 | |
*/ private String secretKey; | |
/** | |
* 域名 | |
*/ private String nginxHost; | |
} |
创建 minio 客户端
package com.sqm.minio_demo.config; | |
import io.minio.MinioClient; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.boot.context.properties.EnableConfigurationProperties; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
public class MinioConfig { | |
private MinioProperties minioProperties; | |
public MinioClient minioClient(){ | |
return MinioClient.builder() | |
.endpoint(minioProperties.getEndpoint()) | |
.credentials(minioProperties.getAccessKey(),minioProperties.getSecretKey()) | |
.build(); | |
} | |
} |
文件地址返回路径实体类
package com.sqm.minio_demo.entity; | |
import lombok.AllArgsConstructor; | |
import lombok.Data; | |
import lombok.NoArgsConstructor; | |
public class UploadResponse { | |
private String minIoUrl; | |
private String nginxUrl; | |
} |
上传文件工具类
package com.sqm.minio_demo.util; | |
import cn.hutool.core.date.DateUtil; | |
import com.sqm.minio_demo.config.MinioProperties; | |
import com.sqm.minio_demo.entity.UploadResponse; | |
import io.minio.*; | |
import io.minio.errors.*; | |
import io.minio.messages.Bucket; | |
import lombok.extern.slfj.Slf4j; | |
import net.coobird.thumbnailator.Thumbnails; | |
import org.apache.commons.fileupload.FileItem; | |
import org.apache.commons.fileupload.FileItemFactory; | |
import org.apache.commons.fileupload.disk.DiskFileItemFactory; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.stereotype.Component; | |
import org.springframework.web.multipart.MultipartFile; | |
import org.springframework.web.multipart.commons.CommonsMultipartFile; | |
import java.io.File; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.OutputStream; | |
import java.security.InvalidKeyException; | |
import java.security.NoSuchAlgorithmException; | |
import java.util.List; | |
import java.util.Optional; | |
import java.util.Random; | |
public class MinioUtil { | |
private MinioProperties minioProperties; | |
private MinioClient minioClient; | |
private final Long maxSize = (long) ( * 1024); | |
/** | |
* 创建bucket | |
*/ public void createBucket(String bucketName) throws Exception { | |
if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) { | |
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); | |
} | |
} | |
/** | |
* 上传文件 | |
*/ public UploadResponse uploadFile(MultipartFile file, String bucketName) throws Exception { | |
//判断文件是否为空 | |
if (null == file || == file.getSize()) { | |
return null; | |
} | |
//判断存储桶是否存在 不存在则创建 | |
createBucket(bucketName); | |
//文件名 | |
String originalFilename = file.getOriginalFilename(); | |
//新的文件名 = 时间戳_随机数.后缀名 | |
assert originalFilename != null; | |
long now = System.currentTimeMillis() /; | |
String fileName = DateUtil.format(DateUtil.date(),"yyyyMMdd")+"_"+ now + "_" + new Random().nextInt() + | |
originalFilename.substring(originalFilename.lastIndexOf(".")); | |
//开始上传 | |
log.info("file压缩前大小:{}",file.getSize()); | |
if (file.getSize() > maxSize) { | |
FileItemFactory fileItemFactory = new DiskFileItemFactory(); | |
FileItem fileItem = fileItemFactory.createItem(fileName, "text/plain", true, fileName); | |
OutputStream outputStream = fileItem.getOutputStream(); | |
Thumbnails.of(file.getInputStream()).scale(f).outputFormat(originalFilename.substring(originalFilename.lastIndexOf(".")+1)).outputQuality(0.25f).toOutputStream(outputStream); | |
file = new CommonsMultipartFile(fileItem); | |
} | |
log.info("file压缩后大小:{}",file.getSize()); | |
minioClient.putObject( | |
PutObjectArgs.builder().bucket(bucketName).object(fileName).stream( | |
file.getInputStream(), file.getSize(), -) | |
.contentType(file.getContentType()) | |
.build()); | |
String url = minioProperties.getEndpoint() + "/" + bucketName + "/" + fileName; | |
String urlHost = minioProperties.getNginxHost() + "/" + bucketName + "/" + fileName; | |
return new UploadResponse(url, urlHost); | |
} | |
/** | |
* 获取全部bucket | |
* | |
* @return | |
*/ public List<Bucket> getAllBuckets() throws Exception { | |
return minioClient.listBuckets(); | |
} | |
/** | |
* 根据bucketName获取信息 | |
* | |
* @param bucketName bucket名称 | |
*/ public Optional<Bucket> getBucket(String bucketName) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidResponseException, InternalException, ErrorResponseException, ServerException, XmlParserException, ServerException { | |
return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst(); | |
} | |
/** | |
* 根据bucketName删除信息 | |
* | |
* @param bucketName bucket名称 | |
*/ public void removeBucket(String bucketName) throws Exception { | |
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); | |
} | |
/** | |
* 获取⽂件外链 | |
* | |
* @param bucketName bucket名称 | |
* @param objectName ⽂件名称 | |
* @param expires 过期时间 <= | |
* @return url | |
*/ public String getObjectURL(String bucketName, String objectName, Integer expires) throws Exception { | |
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(objectName).expiry(expires).build()); | |
} | |
/** | |
* 获取⽂件 | |
* | |
* @param bucketName bucket名称 | |
* @param objectName ⽂件名称 | |
* @return ⼆进制流 | |
*/ public InputStream getObject(String bucketName, String objectName) throws Exception { | |
return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build()); | |
} | |
/** | |
* 上传⽂件 | |
* | |
* @param bucketName bucket名称 | |
* @param objectName ⽂件名称 | |
* @param stream ⽂件流 | |
* @throws Exception #putObject | |
*/ public void putObject(String bucketName, String objectName, InputStream stream) throws | |
Exception { | |
minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, stream.available(), -).contentType(objectName.substring(objectName.lastIndexOf("."))).build()); | |
} | |
/** | |
* 上传⽂件 | |
* | |
* @param bucketName bucket名称 | |
* @param objectName ⽂件名称 | |
* @param stream ⽂件流 | |
* @param size ⼤⼩ | |
* @param contextType 类型 | |
* @throws Exception #putObject | |
*/ public void putObject(String bucketName, String objectName, InputStream stream, long | |
size, String contextType) throws Exception { | |
minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, size, -).contentType(contextType).build()); | |
} | |
/** | |
* 获取⽂件信息 | |
* | |
* @param bucketName bucket名称 | |
* @param objectName ⽂件名称 | |
* @throws Exception #statObject | |
*/ public StatObjectResponse getObjectInfo(String bucketName, String objectName) throws Exception { | |
return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()); | |
} | |
/** | |
* 删除⽂件 | |
* | |
* @param bucketName bucket名称 | |
* @param objectName ⽂件名称 | |
* @throws Exception #removeObject | |
*/ public void removeObject(String bucketName, String objectName) throws Exception { | |
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); | |
} | |
/*** | |
* 上传视频 | |
* @param file | |
* @param bucketName | |
* @return | |
* @throws Exception | |
*/ public UploadResponse uploadVideo(MultipartFile file, String bucketName) throws Exception { | |
//判断文件是否为空 | |
if (null == file || == file.getSize()) { | |
return null; | |
} | |
//判断存储桶是否存在 不存在则创建 | |
createBucket(bucketName); | |
//文件名 | |
String originalFilename = file.getOriginalFilename(); | |
//新的文件名 = 时间戳_随机数.后缀名 | |
assert originalFilename != null; | |
long now = System.currentTimeMillis() /; | |
String fileName = DateUtil.format(DateUtil.date(),"yyyyMMdd")+"_"+ now + "_" + new Random().nextInt() + | |
originalFilename.substring(originalFilename.lastIndexOf(".")); | |
//开始上传 | |
log.info("file大小:{}",file.getSize()); | |
minioClient.putObject( | |
PutObjectArgs.builder().bucket(bucketName).object(fileName).stream( | |
file.getInputStream(), file.getSize(), -) | |
.contentType("video/mp") | |
.build()); | |
String url = minioProperties.getEndpoint() + "/" + bucketName + "/" + fileName; | |
String urlHost = minioProperties.getNginxHost() + "/" + bucketName + "/" + fileName; | |
return new UploadResponse(url, urlHost); | |
} | |
} |
测试上传文件 Controller
package com.sqm.minio_demo.controller; | |
import com.sqm.minio_demo.entity.ResultData; | |
import com.sqm.minio_demo.entity.UploadResponse; | |
import com.sqm.minio_demo.util.MinioUtil; | |
import lombok.extern.slfj.Slf4j; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.web.bind.annotation.PostMapping; | |
import org.springframework.web.bind.annotation.RequestParam; | |
import org.springframework.web.bind.annotation.RestController; | |
import org.springframework.web.multipart.MultipartFile; | |
public class TestController { | |
private MinioUtil minioUtil; | |
/** | |
* @author: xx | |
* @date:/5/25 15:32 | |
* @description: 上传文件 | |
*/ | |
public ResultData minioUpload( MultipartFile file){ | |
UploadResponse response = null; | |
try { | |
response = minioUtil.uploadFile(file, "bucket"); | |
} catch (Exception e) { | |
log.error("上传失败",e); | |
} | |
return ResultData.ok(response); | |
} | |
/** | |
* @author: xx | |
* @date:/5/25 15:32 | |
* @description: 上传视频 | |
*/ | |
public ResultData uploadVideo( MultipartFile file){ | |
UploadResponse response = null; | |
try { | |
response = minioUtil.uploadVideo(file, "video-test"); | |
} catch (Exception e) { | |
log.error("上传失败",e); | |
} | |
return ResultData.ok(response); | |
} | |
} |
测试上传
控制台也可以看到上传的视频