minio项目结构
pom引用
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
项目配置MinIOService
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.cp.demo.constant.ResultVO;
import com.cp.demo.constant.ResultVOUtil;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.rowset.serial.SerialException;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @author chenp
* @date 2023/9/15
*/
@Data
@Component
@Slf4j
public class MinIOService {
@Value("${minio.address}")
private String address;
@Value("${minio.accessKey}")
private String accessKey;
@Value("${minio.secretKey}")
private String secretKey;
@Value("${minio.bucketName}")
private String bucketName;
public MinioClient getMinioClient() {
try {
return MinioClient.builder()
.endpoint(address)
.credentials(accessKey,secretKey)
.build();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 检查存储桶是否存在
*
* @param bucketName 存储桶名称
* @return
*/
public boolean bucketExists(MinioClient minioClient, String bucketName) {
boolean flag = false;
try {
flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (flag) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return false;
}
// Upload '/home/user/Photos/asiaphotos.zip' as object name 'asiaphotos-2015.zip' to bucket
// 'asiatrip'.
/**
* 创建存储桶
*
* @param bucketName 存储桶名称
*/
public boolean makeBucket(MinioClient minioClient, String bucketName) {
try {
boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
//存储桶不存在则创建存储桶
if (!found) {
// 如果不存在创建桶
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 上传文件
*
* @param file 上传文件
* @return 成功则返回文件名,失败返回空
*/
public ResultVO uploadFile(MinioClient minioClient, MultipartFile file) throws SerialException {
//创建存储桶
boolean createFlag = makeBucket(minioClient, bucketName);
//创建存储桶失败
if (createFlag == false) {
throw new SerialException("创建存储桶失败");
}
try {
String originalFilename = file.getOriginalFilename();
int pointIndex = originalFilename.lastIndexOf(".");
//得到文件流
InputStream inputStream = file.getInputStream();
//保证文件不重名(并且没有特殊字符)
String fileName = bucketName+ DateUtil.format(new Date(), "_yyyy-MM-dd-HH-mm-ss") + (pointIndex > -1 ? originalFilename.substring(pointIndex) : "");
minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(fileName).contentType(ViewContentType.getContentType(fileName)).stream(inputStream,inputStream.available(),-1).build());
inputStream.close();
return ResultVOUtil.success(fileName);
} catch (Exception e) {
e.printStackTrace();
return ResultVOUtil.error();
}
}
/**
* 下载文件
*
* @param originalName 文件路径*/
public InputStream downloadFile(MinioClient minioClient, String originalName, HttpServletResponse response) {
try {
// 2. 获取对象的InputStream,并保存为文件
InputStream file = minioClient.getObject(
GetObjectArgs.builder().bucket(bucketName).object(originalName).build());
String filename = new String(originalName.getBytes("ISO8859-1"), StandardCharsets.UTF_8);
if (StrUtil.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 fileName 文件路径
* @return
*/
public boolean deleteFile(MinioClient minioClient, String fileName) {
try {
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* Description 生成预览链接,最大7天有效期;
* 如果想永久有效,在 minio 控制台设置仓库访问规则总几率
* Create by Mr.Fang
*
* @param object 文件名称
* @param contentType 预览类型 image/gif", "image/jpeg", "image/jpg", "image/png", "application/pdf
* @Params
**/
/**
* @description:获取文件预览接口
* @date: 2022/8/18 9:44
* @param: fileName: 文件名
* @Param: bucketName: 桶名
* @Param: previewExpiry: 预览到期时间(小时)
* @return: java.lang.String
**/
public String getPreviewUrl(MinioClient minioClient,String fileName,Integer previewExpiry) {
if (StringUtils.isNotBlank(fileName)) {
bucketName = StringUtils.isNotBlank(bucketName) ? bucketName : this.bucketName;
try {
minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
if (null != previewExpiry){
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(fileName)
.expiry(previewExpiry, TimeUnit.HOURS)
.build());
}else {
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(fileName)
.build());
}
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* 查询所有桶文件
*
* @return
*/
public List<Bucket> getListBuckets(MinioClient minioClient) {
try {
return minioClient.listBuckets();
} catch (Exception e) {
e.printStackTrace();
}
return Collections.emptyList();
}
/**
* 批量删除文件
* @param objects 对象名称
* @return boolean
*/
public List<DeleteError> deleteObject(MinioClient minioClient,List<String> objects) {
List<DeleteError> deleteErrors = new ArrayList<>();
List<DeleteObject> deleteObjects = objects.stream().map(value -> new DeleteObject(value)).collect(Collectors.toList());
Iterable<Result<DeleteError>> results =
minioClient.removeObjects(
RemoveObjectsArgs
.builder()
.bucket(bucketName)
.objects(deleteObjects)
.build());
try {
for (Result<DeleteError> result : results) {
DeleteError error = result.get();
deleteErrors.add(error);
}
} catch (Exception e) {
e.printStackTrace();
}
return deleteErrors;
}
/**
* 获取单个桶中的所有文件对象名称
*
* @param bucket 桶名称
* @return {@link List}<{@link String}>
*/
public List<String> getBucketObjectName(MinioClient minioClient,String bucket) {
boolean exsit = makeBucket(minioClient,bucket);
if (exsit) {
List<String> listObjetcName = new ArrayList<>();
try {
Iterable<Result<Item>> myObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket).build());
for (Result<Item> result : myObjects) {
Item item = result.get();
listObjetcName.add(item.objectName());
}
return listObjetcName;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* 获取某个桶下某个对象的URL
*
* @param objectName 对象名 (文件夹名 )
* @return
*/
public String getBucketObject(MinioClient minioClient, String objectName) throws Exception {
// 删除之前先检查`my-bucket`是否存在。
boolean found = makeBucket(minioClient,bucketName);
if (found) {
final GetObjectResponse object = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
return object.object();
}
return "";
}
/**
* @description: 批量下载
* @param: filenames: 多个文件名称
* @Param: zip: 压缩包名称
* @Param: res: 响应对象
* @return: void
**/
public void batchDownload(MinioClient minioClient, List<String> filenames, String zip, HttpServletResponse res, HttpServletRequest req){
try {
BucketExistsArgs bucketArgs = BucketExistsArgs.builder().bucket(bucketName).build();
boolean bucketExists = minioClient.bucketExists(bucketArgs);
BufferedOutputStream bos = null;
res.reset();
bos = new BufferedOutputStream(res.getOutputStream());
ZipOutputStream out = new ZipOutputStream(bos);
res.setHeader("Access-Control-Allow-Origin", "*");
for (int i=0;i<filenames.size();i++) {
GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName)
.object(filenames.get(i)).build();
InputStream object = minioClient.getObject(objectArgs);
byte buf[] = new byte[1024];
int length = 0;
res.setCharacterEncoding("utf-8");
res.setContentType("application/force-download");// 设置强制下载不打开
res.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
res.setHeader("Content-Disposition", "attachment;filename=" + filenameEncoding(zip,req) + ".zip");
out.putNextEntry(new ZipEntry(filenames.get(i)));
while ((length = object.read(buf)) > 0) {
out.write(buf, 0, length);
}
}
out.close();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 设置不同浏览器编码
* @param filename 文件名称
* @param request 请求对象
*/
public static String filenameEncoding(String filename, HttpServletRequest request) throws UnsupportedEncodingException {
// 获得请求头中的User-Agent
String agent = request.getHeader("User-Agent");
// 根据不同的客户端进行不同的编码
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}
ViewContentType
import cn.hutool.core.util.StrUtil;
/**
* @author chenp
* @date 2023/9/15
*/
public enum ViewContentType {
DEFAULT("default","application/octet-stream"),
JPG("jpg", "image/jpeg"),
TIFF("tiff", "image/tiff"),
GIF("gif", "image/gif"),
JFIF("jfif", "image/jpeg"),
PNG("png", "image/png"),
TIF("tif", "image/tiff"),
ICO("ico", "image/x-icon"),
JPEG("jpeg", "image/jpeg"),
WBMP("wbmp", "image/vnd.wap.wbmp"),
FAX("fax", "image/fax"),
NET("net", "image/pnetvue"),
JPE("jpe", "image/jpeg"),
RP("rp", "image/vnd.rn-realpix");
private String prefix;
private String type;
public static String getContentType(String prefix){
if(StrUtil.isEmpty(prefix)){
return DEFAULT.getType();
}
prefix = prefix.substring(prefix.lastIndexOf(".") + 1);
for (ViewContentType value : ViewContentType.values()) {
if(prefix.equalsIgnoreCase(value.getPrefix())){
return value.getType();
}
}
return DEFAULT.getType();
}
ViewContentType(String prefix, String type) {
this.prefix = prefix;
this.type = type;
}
public String getPrefix() {
return prefix;
}
public String getType() {
return type;
}
}
FileController
import com.cp.demo.config.MinIOService;
import com.cp.demo.constant.ResultVO;
import com.cp.demo.constant.ResultVOUtil;
import io.minio.MinioClient;
import io.minio.messages.DeleteError;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.rowset.serial.SerialException;
import java.util.ArrayList;
import java.util.List;
/**
* @author chenp
* @date 2023/9/15
*/
@RequestMapping("/file")
@RestController
public class FileController {
@Autowired
private MinIOService minIOService;
/**
* 上传文件
*
* @param file 文件
* @return
*/
@PostMapping("/uploadFile")
public ResultVO uploadFile(@RequestBody MultipartFile file) throws SerialException {
MinioClient minioClient = minIOService.getMinioClient();
if (minioClient == null) {
throw new SerialException("连接MinIO服务器失败");
}
return minIOService.uploadFile(minioClient, file);
}
/*
*
* 下载文件
*
* @param response 返回请求
* @param fileName 文件路径
* @return
*/
@GetMapping("/downloadFile")
public String downloadFile(HttpServletResponse response,@RequestParam String fileName) throws SerialException {
MinioClient minioClient = minIOService.getMinioClient();
if (minioClient == null) {
throw new SerialException("连接MinIO服务器失败");
}
return minIOService.downloadFile(minioClient, fileName, response) != null ? "下载成功" : "下载失败";
}
/**
* 删除文件
*
* @param fileName 文件路径
* @return
*/
@DeleteMapping("/deleteFile/{fileName}")
public ResultVO deleteFile(@PathVariable("fileName") String fileName) throws SerialException {
MinioClient minioClient = minIOService.getMinioClient();
if (minioClient == null) {
throw new SerialException("连接MinIO服务器失败");
}
boolean flag = minIOService.deleteFile(minioClient, fileName);
return flag == Boolean.TRUE ? ResultVOUtil.success("删除成功") : ResultVOUtil.success("删除失败");
}
/**
* 文件预览*/
@GetMapping("/prevpreviewFile/{fileName}")
public ResultVO previewFile(@PathVariable(value = "fileName",required = true) String fileName) throws SerialException {
MinioClient minioClient = minIOService.getMinioClient();
if (minioClient == null) {
throw new SerialException("连接MinIO服务器失败");
}
Integer previewExpiry=1;
String previewUrl = minIOService.getPreviewUrl(minioClient, fileName, previewExpiry);
return ResultVOUtil.success(previewUrl);
}
/**
* 获取某个桶下某个对象的URL
* */
@GetMapping("/getBucketObject/{fileName}")
public ResultVO getBucketObject(@PathVariable(value = "fileName",required = true) String fileName) throws Exception {
MinioClient minioClient = minIOService.getMinioClient();
if (minioClient == null) {
throw new SerialException("连接MinIO服务器失败");
}
return ResultVOUtil.success( minIOService.getBucketObject(minioClient, fileName));
}
/**
* 批量删除某个文件
* */
@PostMapping("/getRemoveObjects")
public ResultVO getRemoveObjects(@RequestBody ArrayList<String> objectNames) throws SerialException {
MinioClient minioClient = minIOService.getMinioClient();
if (minioClient == null) {
throw new SerialException("连接MinIO服务器失败");
}
List<DeleteError> deleteErrors = minIOService.deleteObject(minioClient, objectNames);
return ResultVOUtil.success(deleteErrors);
}
/*多文件上传**/
@PostMapping("/uploadAllFile")
public ResultVO upload(@RequestBody MultipartFile[] files) throws Exception {
MinioClient minioClient = minIOService.getMinioClient();
if (minioClient == null) {
throw new SerialException("连接MinIO服务器失败");
}
for (MultipartFile file : files) {
minIOService.uploadFile(minioClient, file);
}
return ResultVOUtil.success();
}
@PostMapping("/batchDownload")
public void batchDownload(@RequestBody() List<String> fileNames,HttpServletResponse res, HttpServletRequest req) throws SerialException {
MinioClient minioClient = minIOService.getMinioClient();
if (minioClient == null) {
throw new SerialException("连接MinIO服务器失败");
}
minIOService.batchDownload(minioClient,fileNames,"1.zip",res,req);
}
}
返回类 ResultVOUtil ResultVO
/**
* @author chenp
* @date 2023/09/01
*/
public class ResultVO {
private static final long serialVersionUID = 1L;
private Object data;//信息
private Object code;//错误码
private Object msg;//错误提示
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public Object getCode() {
return code;
}
public void setCode(Object code) {
this.code = code;
}
public Object getMsg() {
return msg;
}
public void setMsg(Object msg) {
this.msg = msg;
}
}
/**
* @Description: 返回结果封装类
* @UpdateRemark: 修改内容
*/
public class ResultVOUtil {
private ResultVOUtil() {
}
private static final String success = "success";
private static final String code = "0";
//不带返回数据
public static ResultVO success() {
ResultVO result = new ResultVO();
result.setMsg(success);
result.setCode(code);
return result;
}
//带返回数据
public static ResultVO success(Object Data) {
ResultVO result = new ResultVO();
result.setData(Data);
result.setCode(code);
result.setMsg(success);
return result;
}
public static ResultVO success(Integer code, Object data, String message) {
ResultVO result = new ResultVO();
result.setCode(code);
result.setData(data);
result.setMsg(success);
return result;
}
public static ResultVO error() {
ResultVO result = new ResultVO();
result.setCode("-1");
result.setMsg("fail");
return result;
}
public static ResultVO error(Integer code, String message) {
ResultVO result = new ResultVO();
result.setCode(code);
result.setMsg(message);
return result;
}
public static ResultVO error(Object data) {
ResultVO result = new ResultVO();
result.setCode(-1);
result.setMsg("");
result.setData(data);
return result;
}
public static ResultVO errorDate(Object data, String msg) {
ResultVO result = new ResultVO();
result.setData(data);
result.setMsg(msg);
result.setCode(-1);
return result;
}
/* public static ResultVO error(ResultCodesEnum resultCode) {
ResultVO result = new ResultVO();
result.setCode(resultCode.getCode());
result.setMsg(resultCode.getMsg());
return result;
}*/
}