1、Minio
MinIO 是一款基于 Go 语言发开的高性能、分布式的对象存储系统。简单来说:就是可以存视频、图片 等。
通过 github 下载 Minio 服务端:
https://github.com/minio/minio
启动 Minio 服务端:
- 在 minio.exe 目录下打开命令行,执行
minio.exe server 保存路径
。如:minio.exe server D:\profession\behindEnd\minio\data
。那么,上传的文件将会保存在:D:\profession\behindEnd\minio\data
下
启动后,通过 http://127.0.0.1:9000
访问,账号/密码:minioadmin
可以创建一个 bucket。
SpringBoot 集成 Minio:
1、添加依赖:
<!--minio-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.0.3</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>1.3.50</version>
</dependency>
2、添加配置:
spring:
servlet:
multipart:
enabled: true
max-file-size: 10MB # 单个文件上传的最大上限
max-request-size: 10MB # 一次请求总大小上限
minio:
endpoint: http://127.0.0.1:9000
accessKey: minioadmin
secretKey: minioadmin
bucketName: test
3、代码:
配置类:
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioClientConfig {
private String endPoint;
private String accessKey;
private String secretKey;
private String bucketName;
@Bean
public MinioClient minioClient() {
return MinioClient.builder().endpoint(endPoint).credentials(accessKey, secretKey).build();
}
}
工具类:
@Component
public class MinioUtil {
@Autowired
private MinioClientConfig minioClientConfig;
@Autowired
private MinioClient minioClient;
/**
* @Description:判断bucket是否存在,不存在则创建
* @Author: zzc
* @Date: 2022-08-31 15:33
* @param name:
* @return: void
**/
public void existBucket(String name) {
try {
boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
if (!exists) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Description:创建存储bucket
* @Author: zzc
* @Date: 2022-08-31 15:33
* @param name:
* @return: java.lang.Boolean
**/
public Boolean makeBucket(String name) {
try {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 删除存储bucket
* @param name 存储bucket名称
* @return Boolean
*/
public Boolean removeBucket(String name) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder()
.bucket(name)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
public String upload(MultipartFile file) {
return upload(Arrays.asList(file)).get(0);
}
/**
* @Description:批量上传文件
* @Author: zzc
* @Date: 2022-08-31 15:43
* @param multipartFile:
* @return: java.util.List<java.lang.String>
**/
public List<String> upload(List<MultipartFile> multipartFile) {
List<String> urls = new ArrayList<>(multipartFile.size());
for (MultipartFile file : multipartFile) {
String fileName = file.getOriginalFilename();
String[] split = fileName.split("\\.");
if (split.length > 1) {
fileName = split[0] + "_" + System.currentTimeMillis() + "." + split[1];
} else {
fileName = fileName + System.currentTimeMillis();
}
InputStream in = null;
try {
in = file.getInputStream();
minioClient.putObject(PutObjectArgs.builder()
.bucket(minioClientConfig.getBucketName())
.object(fileName)
.stream(in, in.available(), -1)
.contentType(file.getContentType())
.build()
);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
urls.add(minioClientConfig.getEndPoint() + "/" + minioClientConfig.getBucketName() + "/" + fileName);
}
return urls;
}
/**
* @Description:下载文件
* @Author: zzc
* @Date: 2022-08-31 15:46
* @param fileName:
* @return: org.springframework.http.ResponseEntity<byte[]>
**/
public ResponseEntity<byte[]> download(String fileName) {
ResponseEntity<byte[]> responseEntity = null;
InputStream in = null;
ByteArrayOutputStream out = null;
try {
in = minioClient.getObject(GetObjectArgs.builder().bucket(minioClientConfig.getBucketName()).object(fileName).build());
out = new ByteArrayOutputStream();
IOUtils.copy(in, out);
//封装返回值
byte[] bytes = out.toByteArray();
HttpHeaders headers = new HttpHeaders();
try {
headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
headers.setContentLength(bytes.length);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setAccessControlExposeHeaders(Arrays.asList("*"));
responseEntity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return responseEntity;
}
/**
* @Description:查看bucketName里面的文件对象
* @Author: zzc
* @Date: 2022-08-31 15:48
* @param bucketName:
* @return: java.util.List<com.zzc.hardcore.vo.ObjectItemVo> 存储bucket内文件对象信息
**/
public List<ObjectItemVo> listObjects(String bucketName) {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).build());
List<ObjectItemVo> objectItems = new ArrayList<>();
try {
for (Result<Item> result : results) {
Item item = result.get();
ObjectItemVo objectItem = new ObjectItemVo();
objectItem.setObjectName(item.objectName());
objectItem.setSize(item.size());
objectItems.add(objectItem);
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return objectItems;
}
/**
* @Description:批量删除文件对象
* @Author: zzc
* @Date: 2022-08-31 15:49
* @param bucketName:
* @param objects: 对象名称集合
* @return: java.lang.Iterable<io.minio.Result<io.minio.messages.DeleteError>>
**/
public Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> objects) {
List<DeleteObject> dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());
Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
return results;
}
}
@Data
public class ObjectItemVo {
private String objectName;
private Long size;
}
4、接口测试
@RestController
@RequestMapping("/minio")
public class MinioController {
@Autowired
private MinioUtil minioUtil;
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) {
return minioUtil.upload(file);
}
@PostMapping("/download")
public ResponseEntity<byte[]> download() {
return minioUtil.download("微信图片_20211002143943_1661932958068.jpg");
}
}
2、OSS
引入依赖:
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.11.2</version>
</dependency>
配置文件
oss:
aliyun:
# 在控制台 创建完 bucket 后,会自动生成的
endPoint: ''
# 注册后会有
accessKeyId: ''
# 注册后会有
accessKeySecret: ''
# 自己在控制台创建
bucketName: ''
配置类:
@Data
@Component
@ConfigurationProperties(prefix = "oss.aliyun")
public class OssConfig implements InitializingBean {
// 创建了bucket之后,oss会根据选择的区域自动分配endpoint
private String endPoint;
// accessKeyId
private String accessKeyId;
// accessKeySecret
private String accessKeySecret;
// bucket名称
private String bucketName;
public static String END_POINT;
public static String ACCESS_KEY_ID;
public static String ACCESS_KEY_SECRET;
public static String BUCKET_NAME;
@Override
public void afterPropertiesSet() {
END_POINT = endPoint;
ACCESS_KEY_ID = accessKeyId;
ACCESS_KEY_SECRET = accessKeySecret;
BUCKET_NAME = bucketName;
}
}
工具类:
public class OssUtil {
/**
* 操作OSS对象
*/
private static OSSClient ossClient = null;
static {
initOssClient(OssConfig.END_POINT, OssConfig.ACCESS_KEY_ID, OssConfig.ACCESS_KEY_SECRET);
}
/**
* 初始化
*
* @author zzc
* @date 2023/2/1 14:40
* @param: endPoint
* @param: accessKeyId
* @param: accessKeySecret
**/
private static void initOssClient(String endPoint, String accessKeyId, String accessKeySecret) {
if (Objects.isNull(ossClient)) {
ossClient = new OSSClient(endPoint, new DefaultCredentialProvider(accessKeyId, accessKeySecret), new ClientConfiguration());
}
}
/**
* 上产文件
*
* @author zzc
* @date 2023/2/1 15:12
* @param: file
* @param: fileDir
* @return: java.lang.String
**/
public static String upload(MultipartFile file, String fileDir) {
// 1,判断bucketName是否存在
if (!ossClient.doesBucketExist(OssConfig.BUCKET_NAME)) {
ossClient.createBucket(OssConfig.BUCKET_NAME);
}
// 2.获取文件名称
String fileName = getFileName(file);
StringBuilder fileUrl = new StringBuilder();
if (StringUtils.isNotBlank(fileDir)) {
fileDir = fileDir.endsWith("/") ? fileDir : fileDir.concat("/");
fileUrl.append(fileDir);
}
fileUrl.append(fileName);
// 3.获取文件访问路径
String filePath = getFilePath(fileUrl.toString());
// 4.上传到OSS服务器
try {
PutObjectResult result = ossClient.putObject(OssConfig.BUCKET_NAME, fileUrl.toString(), file.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
return filePath;
}
/**
* 删除文件
*
* @author zzc
* @date 2023/2/1 15:14
* @param: fileName
* @return: boolean
**/
public static boolean delete(String fileName) {
try {
if (!ossClient.doesBucketExist(OssConfig.BUCKET_NAME)) {
return false;
}
ossClient.deleteObject(OssConfig.BUCKET_NAME, fileName);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private static String getFilePath(String fileUrl) {
return "https://" + OssConfig.BUCKET_NAME + "." + OssConfig.END_POINT + "/" + fileUrl;
}
private static String getFileName(MultipartFile file) {
String orgName = file.getOriginalFilename();
if (StringUtils.isBlank(orgName)) {
orgName = file.getName();
}
// 对文件类型检查
/*try {
FileTypeFilter.fileTypeFilter(file);
} catch (Exception e) {
e.printStackTrace();
}*/
orgName = getFileName(orgName);
return !orgName.contains(".") ? System.currentTimeMillis() + "" : System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
}
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;
}
}
测试:
@RestController
@RequestMapping("/oss")
public class OssController {
@PostMapping("/upload")
public String uploadOssFile(@RequestParam("file") MultipartFile file, @RequestParam("filePath") String filePath) {
return OssUtil.upload(file, filePath);
}
}