一、minio簡介
MinIO 是在 GNU Affero 通用公共許可證 v3.0 下發布的高效能物件儲存。 它是與 Amazon S3 雲端儲存服務相容的 API,非常適合於儲存大容量非結構化的資料,例如圖片、視訊、日誌檔案、備份資料和容器/虛擬機器映象等,而一個物件檔案可以是任意大小,從幾kb到最大5T不等
二、minio安裝
參考我的另一篇文章:https://www.cnblogs.com/lvlinguang/p/15770479.html
一、java中使用
1、pom檔案引用
<!-- minio 檔案服務客戶端 -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>6.0.11</version>
</dependency>
2、新增配置檔案
- url:minio伺服器的介面地址,不是訪問地址
- accessKey/secretKey:登入minio系統,新建Service Accounts
config:
minio:
url: http://192.168.3.15:9000
accessKey: 66SBZWYDSO0DZRSE1U3T
secretKey: S+p8mWE8aykZ0YsRtC0ef35qUS7fUbkITITJdjS6
3、註冊MinioClient
@Data
@Configuration
@ConfigurationProperties(prefix = "config.minio")
public class MinioConfig {
/**
* minio 服務地址 http://ip:port
*/
private String url;
/**
* 使用者名稱
*/
private String accessKey;
/**
* 密碼
*/
private String secretKey;
@SneakyThrows
@Bean
@RefreshScope
public MinioClient minioClient(){
return new MinioClient(url, accessKey, secretKey);
}
}
4、minio工具類
/**
* minio工具類
*
* @author lvlinguang
* @date 2022-01-07 12:26
* @see http://docs.minio.org.cn/docs/master/java-client-api-reference
*/
@Component
public class MinioUtils {
@Autowired
private MinioClient client;
/**
* 建立bucket
*
* @param bucketName
*/
@SneakyThrows
public void createBucket(String bucketName) {
if (!client.bucketExists(bucketName)) {
client.makeBucket(bucketName);
}
}
/**
* 獲取所有bucket
*
* @return
*/
@SneakyThrows
public List<Bucket> listAllBuckets() {
return client.listBuckets();
}
/**
* bucket詳情
*
* @param bucketName 名稱
* @return
*/
@SneakyThrows
public Optional<Bucket> getBucket(String bucketName) {
return client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
}
/**
* 刪除bucket
*
* @param bucketName 名稱
*/
@SneakyThrows
public void removeBucket(String bucketName) {
client.removeBucket(bucketName);
}
/**
* 上傳檔案
*
* @param bucketName bucket名稱
* @param objectName 檔名稱
* @param stream 檔案流
* @throws Exception
*/
@SneakyThrows
public void uploadFile(String bucketName, String objectName, InputStream stream) throws Exception {
this.uploadFile(bucketName, objectName, stream, (long) stream.available(), "application/octet-stream");
}
/**
* 上傳檔案
*
* @param bucketName bucket名稱
* @param objectName 檔名稱
* @param stream 檔案流
* @param size 大小
* @param contextType 型別
* @throws Exception
*/
@SneakyThrows
public void uploadFile(String bucketName, String objectName, InputStream stream, long size, String contextType) throws Exception {
//如果bucket不存在,則建立
this.createBucket(bucketName);
client.putObject(bucketName, objectName, stream, size, null, null, contextType);
}
/**
* 獲取檔案
*
* @param bucketName bucket名稱
* @param objectName 檔名稱
* @return
*/
@SneakyThrows
public InputStream getFile(String bucketName, String objectName) {
return client.getObject(bucketName, objectName);
}
/**
* 根據檔案前置查詢檔案
*
* @param bucketName bucket名稱
* @param prefix 字首
* @param recursive 是否遞迴查詢
* @return
*/
@SneakyThrows
public List<MinioItem> listAllFileByPrefix(String bucketName, String prefix, boolean recursive) {
List<MinioItem> objectList = new ArrayList<>();
Iterable<Result<Item>> objectsIterator = client
.listObjects(bucketName, prefix, recursive);
while (objectsIterator.iterator().hasNext()) {
objectList.add(new MinioItem(objectsIterator.iterator().next().get()));
}
return objectList;
}
/**
* 刪除檔案
*
* @param bucketName bucket名稱
* @param objectName 檔名稱
*/
@SneakyThrows
public void removeFile(String bucketName, String objectName) {
client.removeObject(bucketName, objectName);
}
/**
* 獲取檔案外鏈
*
* @param bucketName bucket名稱
* @param objectName 檔名稱
* @param expires 過期時間 <=7
* @return
*/
@SneakyThrows
public String getFileURL(String bucketName, String objectName, Integer expires) {
return client.presignedGetObject(bucketName, objectName, expires);
}
/**
* 獲取檔案資訊
*
* @param bucketName bucket名稱
* @param objectName 檔名稱
* @return
*/
@SneakyThrows
public ObjectStat getFileInfo(String bucketName, String objectName) {
return client.statObject(bucketName, objectName);
}
}
5、檔案操作類
/**
* 系統檔案工具類
*
* @author lvlinguang
* @date 2021-02-28 12:30
*/
@Component
public class SysFileUtils {
/**
* 檔案伺服器中的目錄分隔符
*/
public static final String SEPRETOR = "/";
@Autowired
private MinioUtils minioUtils;
/**
* 檔案上傳
*
* @param object 檔案對你
* @param bucketName bucket名稱
* @return
*/
@SneakyThrows
public MinioObject uploadFile(MultipartFile object, String bucketName) {
return this.uploadFile(object.getInputStream(), bucketName, object.getOriginalFilename());
}
/**
* 檔案上傳
*
* @param object 檔案對你
* @param bucketName bucket名稱
* @param fileName 檔名
* @return
*/
@SneakyThrows
public MinioObject uploadFile(MultipartFile object, String bucketName, String fileName) {
return this.uploadFile(object.getInputStream(), bucketName, fileName);
}
/**
* 檔案上傳
*
* @param object 檔案對你
* @param bucketName bucket名稱
* @param randomFileName 檔名是否隨機(是:按年/月/日/隨機值儲存,否:按原檔名儲存)
* @return
*/
@SneakyThrows
public MinioObject uploadFile(MultipartFile object, String bucketName, Boolean randomFileName) {
//檔名
String fileName = object.getOriginalFilename();
if (randomFileName) {
//副檔名
String extName = FileUtil.extName(object.getOriginalFilename());
if (StrUtil.isNotBlank(extName)) {
extName = StrUtil.DOT + extName;
}
//新檔名
fileName = randomFileName(extName);
}
return this.uploadFile(object.getInputStream(), bucketName, fileName);
}
/**
* 檔案上傳
*
* @param object 檔案對你
* @param bucketName bucket名稱
* @param fileName 檔名
* @return
*/
@SneakyThrows
public MinioObject uploadFile(InputStream object, String bucketName, String fileName) {
try {
minioUtils.uploadFile(bucketName, fileName, object);
return new MinioObject(minioUtils.getFileInfo(bucketName, fileName));
} catch (Exception e) {
throw new Exception(e);
} finally {
if (object != null) {
object.close();
}
}
}
/**
* 下載檔案
*
* @param response response
* @param url 檔案地址(/bucketName/fileName)
*/
public void downloadFile(HttpServletResponse response, String url) {
final String bucketName = getBucketName(url);
final String filePath = getFilePath(url);
this.downloadFile(response, bucketName, filePath);
}
/**
* 下載檔案
*
* @param response response
* @param bucket bucket名稱
* @param fileName 檔名
*/
public void downloadFile(HttpServletResponse response, String bucket, String fileName) {
try (InputStream inputStream = minioUtils.getFile(bucket, fileName)) {
if ("jpg".equals(FileUtil.extName(fileName))) {
response.setContentType("image/jpeg");
} else if ("png".equals(FileUtil.extName(fileName))) {
response.setContentType("image/png");
} else {
response.setContentType("application/octet-stream; charset=UTF-8");
}
IoUtil.copy(inputStream, response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
}
}
/**
* 獲取連結地址的 檔名
*
* @param bucketFileUrl
* @return
*/
public static String getFilePath(String bucketFileUrl) {
if (bucketFileUrl == null) {
return null;
}
//去掉第一個分割符
if (bucketFileUrl.startsWith(SEPRETOR)) {
bucketFileUrl = bucketFileUrl.substring(1);
}
return bucketFileUrl.substring(bucketFileUrl.indexOf(SEPRETOR) + 1);
}
/**
* 獲取連結地址的 bucketName
*
* @param bucketFileUrl 地址(/{bucketName}/{path}/{fileName})
* @return
*/
public static String getBucketName(String bucketFileUrl) {
if (bucketFileUrl == null) {
return null;
}
//去掉第一個分割符
if (bucketFileUrl.startsWith(SEPRETOR)) {
bucketFileUrl = bucketFileUrl.substring(1);
}
return bucketFileUrl.substring(0, bucketFileUrl.indexOf(SEPRETOR));
}
/**
* 生成新的檔名
*
* @param extName 副檔名
* @return
*/
public static String randomFileName(String extName) {
LocalDate now = LocalDate.now();
return now.getYear() + SEPRETOR +
getFullNumber(now.getMonthValue()) + SEPRETOR +
getFullNumber(now.getDayOfMonth()) + SEPRETOR +
UUID.randomUUID().toString().replace("-", "") + extName;
}
/**
* 得到數字全稱,帶0
*
* @param number
* @return
*/
public static String getFullNumber(Integer number) {
if (number < 10) {
return "0" + number;
}
return number.toString();
}
}
5、返回包裝類
@Data
public class MinioItem {
private String objectName;
private Date lastModified;
private String etag;
private Long size;
private String storageClass;
private Owner owner;
private String type;
public MinioItem(Item item) {
this.objectName = item.objectName();
this.lastModified = item.lastModified();
this.etag = item.etag();
this.size = (long) item.size();
this.storageClass = item.storageClass();
this.owner = item.owner();
this.type = item.isDir() ? "directory" : "file";
}
}
@Data
public class MinioObject {
private String bucketName;
private String name;
private Date createdTime;
private Long length;
private String etag;
private String contentType;
public MinioObject(ObjectStat os) {
this.bucketName = os.bucketName();
this.name = os.name();
this.createdTime = os.createdTime();
this.length = os.length();
this.etag = os.etag();
this.contentType = os.contentType();
}
}
6、控制器呼叫
@RestController
@RequestMapping("/file")
public class FileController {
public static final String MAPPING = "/file";
@Autowired
private SysFileUtils sysFileUtils;
@PostMapping("/upload")
public ApiResponse<MinioObject> upload(@RequestPart("file") MultipartFile file, @RequestParam("bucketName") String bucketName) {
final MinioObject minioObject = sysFileUtils.uploadFile(file, bucketName, true);
return ApiResponse.ok(minioObject);
}
@GetMapping("/info/**")
public void getFile(HttpServletRequest request, HttpServletResponse response) {
final String uri = request.getRequestURI();
String fullName = uri.replace(MAPPING + "/info", "");
sysFileUtils.downloadFile(response, fullName);
}
}
7、訪問測試
- 上傳測試
-下載/訪問測試
地址:http://192.168.3.15:30061/info/avatar/2022/01/07/41aaeb9f56e64c10971b2d9c675ce8fe.jpg