目录
gitee:https://gitee.com/pythonloser/springboot-minio/tree/master
一.minio安装与使用:https://blog.csdn.net/xixiyuguang/article/details/119344480
二.minio整合springboot、文件上传下载、增删改查bucket和object:https://blog.csdn.net/xixiyuguang/article/details/119447614
三.minio 的分布式部署、单节点多磁盘、多节点模式:https://blog.csdn.net/xixiyuguang/article/details/119456729
1.效果展示
1.1windows中minio运行
1.2minio管理页面
1.3制作图片服务
2.springboot项目创建
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.yg</groupId>
<artifactId>minio</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>18-springboot-minio</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<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>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<!-- minio -->
<!-- minio 相关依赖 -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<!--<version>3.0.10</version>-->
<version>8.0.3</version>
</dependency>
<!-- alibaba的fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.51</version>
</dependency>
<!-- thymeleaf模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!---->
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20140107</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
properties
# minio 文件存储配置信息
minio.endpoint=http://127.0.0.1:9090
minio.accesskey=admin
minio.secretKey=12345678
spring.thymeleaf.mode=HTML
spring.thymeleaf.prefix=classpath:/templates/
spring.servlet.multipart.max-request-size=500MB
spring.servlet.multipart.max-file-size=500MB
util
package com.yg.minio;
import com.alibaba.fastjson.JSONObject;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
@Slf4j
@Component
public class MinioUtils {
/**
* 默认块大小
* 上传的时候必须设置分块大小。对象大小直接-1,分块大小[5m,5G],参数的单位是B,
* 所以最小单位是:5 * 1024 * 1024 = 5242880。minio支持分块传输
*/
private final long defaultPartSize = 5242880;
@Autowired
private MinioClient minioClient;
@Autowired
private MinioProp minioProp;
/**
* 创建bucket
*
* @param bucketName bucket名称
*/
@SneakyThrows
public void createBucket(String bucketName) {
if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
}
/**
* 获取全部bucket
*/
@SneakyThrows
public List<Bucket> getAllBuckets() {
return minioClient.listBuckets();
}
/**
* 根据bucketName获取信息
*
* @param bucketName bucket名称
*/
@SneakyThrows
public Optional<Bucket> getBucket(String bucketName) {
return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
}
/**
* 根据bucketName删除信息
*
* @param bucketName bucket名称
*/
@SneakyThrows
public void removeBucket(String bucketName) {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
}
/**
* 获取文件外链
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @param expires (单位秒) 过期时间 <=7
* @return url
*/
@SneakyThrows
public String getObjectURL(String bucketName, String objectName, Integer expires) {
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.bucket(bucketName)
.object(objectName)
.expiry(expires)
.method(Method.GET)
.build());
}
/**
* 获取文件
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @return 二进制流
*/
@SneakyThrows
public InputStream getObject(String bucketName, String objectName) {
return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
}
/**
* 上传文件
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @param stream 文件流
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
*/
public void putObject(String bucketName, String objectName, InputStream stream) throws Exception {
minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, -1, defaultPartSize).build());
}
/**
* @param bucketName bucket名称
* @param objectName 文件名称
* @param fileName 上传文件路径
* @throws Exception
*/
public void putObject(String bucketName, String objectName, String fileName) throws Exception {
minioClient.uploadObject(UploadObjectArgs.builder().bucket(bucketName).object(objectName).filename(fileName).build());
}
/**
* 上传文件
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @param stream 文件流
* @param size 大小
* @param contextType 类型
* @throws Exception https://docs.minio.io/cn/java-minioClient-api-reference.html#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, defaultPartSize).contentType(contextType).build());
}
/**
* 获取文件信息
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#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 https://docs.minio.io/cn/java-client-api-reference.html#removeObject
*/
public void removeObject(String bucketName, String objectName) throws Exception {
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
}
/**
* 上传文件
*
* @param file 文件
* @param bucketName 存储桶
* @return
*/
public JSONObject uploadFile(MultipartFile file, String bucketName) throws Exception {
JSONObject res = new JSONObject();
res.put("code", 0);
// 判断上传文件是否为空
if (null == file || 0 == file.getSize()) {
res.put("msg", "上传文件不能为空");
return res;
}
// 判断存储桶是否存在
createBucket(bucketName);
// 文件名
String originalFilename = file.getOriginalFilename();
// 新的文件名
String fileName = bucketName + "_" + System.currentTimeMillis() + originalFilename.substring(originalFilename.lastIndexOf("."));
// 开始上传
minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(file.getInputStream(), -1, defaultPartSize).contentType(file.getContentType()).build());
res.put("code", 1);
res.put("msg", minioProp.getEndpoint() + "/" + bucketName + "/" + fileName);
return res;
}
/**
* 查询policy
* @param bucketName
* @return
* @throws Exception
*/
public String getBucketPolicy(String bucketName) throws Exception {
return minioClient.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build());
}
/**
* 设置policy
* @param bucketName
* @return
* @throws Exception
*/
public void setBucketPolicy(String bucketName) throws Exception {
StringBuilder builder = new StringBuilder();
builder.append("{\n");
builder.append(" \"Statement\": [\n");
builder.append(" {\n");
builder.append(" \"Action\": [\n");
builder.append(" \"s3:GetBucketLocation\",\n");
builder.append(" \"s3:ListBucket\"\n");
builder.append(" ],\n");
builder.append(" \"Effect\": \"Allow\",\n");
builder.append(" \"Principal\": \"*\",\n");
builder.append(" \"Resource\": \"arn:aws:s3:::my-bucketname\"\n");
builder.append(" },\n");
builder.append(" {\n");
builder.append(" \"Action\": \"s3:GetObject\",\n");
builder.append(" \"Effect\": \"Allow\",\n");
builder.append(" \"Principal\": \"*\",\n");
builder.append(" \"Resource\": \"arn:aws:s3:::my-bucketname/myobject*\"\n");
builder.append(" }\n");
builder.append(" ],\n");
builder.append(" \"Version\": \"2012-10-17\"\n");
builder.append("}\n");
minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(builder.toString()).build());
}
}
config
package com.yg.minio;
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;
/**
* minio 核心配置类
*/
@Configuration
@EnableConfigurationProperties(MinioProp.class)
public class MinioConfig {
@Autowired
private MinioProp minioProp;
/**
* 获取 MinioClient
*/
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(minioProp.getEndpoint())
.credentials(minioProp.getAccesskey(), minioProp.getSecretKey())
.build();
}
}
package com.yg.minio;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* minio 属性值
*/
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioProp {
/**
* 连接url
*/
private String endpoint;
/**
* 用户名
*/
private String accesskey;
/**
* 密码
*/
private String secretKey;
}
domain
@XmlRootElement(name = "Album")
public class Album {
private String url;
private String description;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
dao
package com.yg.minio.photo;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.ListObjectsArgs;
import io.minio.MinioClient;
import io.minio.Result;
import io.minio.http.Method;
import io.minio.messages.Item;
import java.util.ArrayList;
import java.util.List;
public class AlbumDao {
public List<Album> listAlbums() throws Exception {
List<Album> list = new ArrayList<Album>();
final String bucket = "album";
// Initialize minio client object.
MinioClient minioClient =
MinioClient.builder()
.endpoint("http://127.0.0.1:9090")
.credentials("admin", "12345678")
.build();
String url;
// List all objects.
Iterable<Result<Item>> myObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket).build());
// Iterate over each elements and set album url.
for (Result<Item> result : myObjects) {
Item item = result.get();
System.out.println(item.lastModified() + ", " + item.size() + ", " + item.objectName());
// Generate a presigned URL which expires in a day
url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucket)
.object(item.objectName()).method(Method.GET).expiry(60 * 60 * 24).build());
Album album = new Album();
album.setUrl(url);
album.setDescription(item.objectName());
list.add(album);
}
// Return list of albums.
return list;
}
}
controller
package com.yg.minio;
import com.yg.minio.photo.Album;
import com.yg.minio.photo.AlbumDao;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@Controller
public class ThymeleafController {
//http://localhost:8080/test
@RequestMapping("/test")
public String index(Model model, HttpServletRequest httpServletRequest) {
return "test";
}
//http://localhost:8080/photo
@RequestMapping("/photo")
public String photo(Model model, HttpServletRequest httpServletRequest) {
try {
AlbumDao albumDao = new AlbumDao();
List<Album> albums = albumDao.listAlbums();
model.addAttribute("albums", albums);
} catch (Exception e) {
e.printStackTrace();
}
return "photo";
}
}
package com.yg.minio;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
@Controller
public class MinioController {
@Autowired
private MinioUtils minioUtils;
/**
* 上传
*
* @param file
* @param request
* @return
*/
@PostMapping("/upload")
@ResponseBody
public String upload(@RequestParam(name = "file", required = false) MultipartFile file, HttpServletRequest request) {
JSONObject res = null;
try {
res = minioUtils.uploadFile(file, "product");
} catch (Exception e) {
e.printStackTrace();
res.put("code", 0);
res.put("msg", "上传失败");
}
return res.toJSONString();
}
/**
* 下载
*/
@GetMapping("/download")
@ResponseBody
public void download(HttpServletResponse httpResponse) {
try {
InputStream product = minioUtils.getObject("product", "product_1628144206698.JPG");
byte buf[] = new byte[1024];
int length = 0;
httpResponse.reset();
httpResponse.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("中文.JPG", "UTF-8"));
httpResponse.setContentType("application/octet-stream");
httpResponse.setCharacterEncoding("utf-8");
OutputStream outputStream = httpResponse.getOutputStream();
while ((length = product.read(buf)) > 0) {
outputStream.write(buf, 0, length);
}
outputStream.close();
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
/**
* ResponseEntity的方式读取文件,设置只能读取图片
* http://127.0.0.1:8080/readFile/1
*/
@GetMapping(value = "/readFile/{fileId}", produces = {"image/jpeg"})
public ResponseEntity readFile(@PathVariable String fileId) {
String objectName = "product_1628144206698.JPG";
InputStream inputStream = minioUtils.getObject("product", objectName);
try {
InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
String fileName = URLEncoder.encode(objectName, "UTF-8");
//文件类型
MediaType mediaType = MediaType.parseMediaType("image/jpeg");
HttpHeaders httpHeaders = new HttpHeaders();
// httpHeaders.setContentDispositionFormData("attachment", fileName); //如果是下载,需要将这个加上
httpHeaders.setContentType(mediaType);
return new ResponseEntity<InputStreamResource>(inputStreamResource, httpHeaders, HttpStatus.OK);
} catch (Exception e) {
System.out.println(e.toString());
return null;
}
}
/***
*ResponseEntity方式下载
* @param fileId
* @param response
* @return
* http://127.0.0.1:8080/downloadFile/1
*/
@GetMapping("/downloadFile/{fileId}")
public ResponseEntity<byte[]> downloadFile(@PathVariable Long fileId, HttpServletResponse response) {
try {
String objectName = "product_1628144206698.JPG";
InputStream inputStream = minioUtils.getObject("product", objectName);
String fileName = URLEncoder.encode(objectName, "UTF-8");
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentDispositionFormData("attachment", fileName);
httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
ResponseEntity responseEntity = new ResponseEntity<>(readInputStream(inputStream), httpHeaders, HttpStatus.OK);
return responseEntity;
} catch (Exception e) {
return null;
}
}
/**
* 输入流装成字节数组
*
* @param inStream
* @return
* @throws Exception
*/
public byte[] readInputStream(InputStream inStream) throws Exception {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
inStream.close();
return outStream.toByteArray();
}
}
@RestController
@RequestMapping("photo")
public class PhotoController {
AlbumDao albumDao = new AlbumDao();
/**
* http://localhost:8080/photo/list
* @return
*/
@RequestMapping("/list")
public List<Album> listAlbums() {
try {
return albumDao.listAlbums();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}