1地梨、Minio安裝
請(qǐng)看我之前撰寫的 Docker安裝Minio存儲(chǔ)服務(wù)器詳解
2、工程架構(gòu)模式
請(qǐng)看我之前撰寫的 Kotlin +SpringBoot + MyBatis完美搭建最簡(jiǎn)潔最酷的前后端分離框架進(jìn)行完善
3 、依賴jar
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>3.0.7</version>
</dependency>
4蔓姚、自動(dòng)裝配
- 在resource在源文件里面添加一個(gè)
spring.factories
配置文件,配置代碼如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.flong.kotlin.core.minio.MinioAutoConfiguration
5、properties配置minio信息
# minio服務(wù)器url
minio.url=http://192.168.1.133:9000
# minio安裝指定的訪問(wèn)key
minio.accessKey=admin
# minio安裝指定的訪問(wèn)秘鑰
minio.secretKey=admin123456
# minio的啟用配置
minio.endpoint.enable=true
6、工程核心代碼如下
-
MinioAutoConfiguration 自動(dòng)裝配類
@EnableConfigurationProperties(MinioProperties::class)
open class MinioAutoConfiguration {
@Autowired lateinit var minioProperties: MinioProperties;
@Bean
@ConditionalOnMissingBean(MinioTemplate::class)
@ConditionalOnProperty(name = arrayOf("minio.url"))
open fun minioTemplate():MinioTemplate {
return MinioTemplate(minioProperties.url,
minioProperties.accessKey,
minioProperties.secretKey)
}
}
-
MinioProperties 配置屬性類
/*
* Value注解的使用
* https://segmentfault.com/a/1190000010978025
* https://blog.jetbrains.com/idea/2018/10/spring-kotlin-references-in-value-annotation/
*/
@Configuration
@ConfigurationProperties(prefix = "minio")
open class MinioProperties {
/**
* minio 服務(wù)地址 http://ip:port
*/
var url:String=""
/**
* 用戶名
*/
var accessKey:String=""
/**
* 密碼
*/
var secretKey:String =""
}
-
MinioTemplate 連接minio服務(wù)器模板工具類
/**
* MinioTemplate連接工具
* Linux的時(shí)區(qū)與minio容器的時(shí)區(qū)不一致導(dǎo)致的異常
* ErrorResponse(code=RequestTimeTooSkewed, message=The difference between the request time and the server's time is too large.,
* bucketName=null, objectName=null, resource=/kotlin, requestId=null, hostId=null)
*/
@Component
@Configuration
@EnableConfigurationProperties(MinioProperties::class)
open class MinioTemplate {
var url:String = ""
var accessKey:String = ""
var secretKey:String = ""
//constructor無(wú)參數(shù)構(gòu)造方法是為了解決以下錯(cuò)誤:
//Unsatisfied dependency expressed through constructor parameter 0; nested exception is
//org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.String'
//available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
constructor()
//有參數(shù)的構(gòu)造方法
constructor(url:String , accessKey:String, secretKey:String){
this.url = url
this.accessKey = accessKey
this.secretKey = secretKey
}
@Autowired lateinit var minioProperties: MinioProperties
open fun getMinioClient(): MinioClient {
return MinioClient(minioProperties.url,minioProperties.accessKey,minioProperties.secretKey)
}
/**
* 創(chuàng)建bucket
* @param bucketName bucket名稱
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#makeBucket
*/
fun createBucket(bucketName: String) {
var client = getMinioClient()
if (!client.bucketExists(bucketName)) {
client.makeBucket(bucketName)
}
}
/**
* 獲取全部bucket
* <p>
* https://docs.minio.io/cn/java-client-api-reference.html#listBuckets
*/
fun getAllBuckets(): List<Bucket> {
return getMinioClient().listBuckets()
}
/**
* @param bucketName bucket名稱
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#listBuckets
*/
fun getBucket(bucketName: String): Optional<Bucket> {
return getMinioClient().listBuckets().stream().filter({ b -> b.name().equals(bucketName) }).findFirst()
}
/**
* @param bucketName bucket名稱
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeBucket
*/
fun removeBucket(bucketName: String) {
getMinioClient().removeBucket(bucketName)
}
/**
* 根據(jù)文件前置查詢文件
* @param bucketName bucket名稱
* @param prefix 前綴
* @param recursive 是否遞歸查詢
* @return
* @throws Exception
*/
fun getAllObjectsByPrefix(bucketName: String, prefix: String, recursive: Boolean): List<Item> {
var objectList = ArrayList<Item>()
var objectsIterator = getMinioClient().listObjects(bucketName, prefix, recursive)
while (objectsIterator.iterator().hasNext()) {
objectList.add(objectsIterator.iterator().next().get())
}
return objectList
}
/**
* 獲取文件外鏈
* @param bucketName bucket名稱
* @param objectName 文件名稱
* @param expires 過(guò)期時(shí)間 <=7
* @return url
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#getObject
*/
fun getObjectURL(bucketName:String , objectName:String , expires:Int ) :String {
return getMinioClient().presignedGetObject(bucketName, objectName, expires)
}
/**
* 獲取文件
* @param bucketName bucket名稱
* @param objectName 文件名稱
* @return 二進(jìn)制流
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#getObject
*/
fun getObject(bucketName:String , objectName:String ) :InputStream{
return getMinioClient().getObject(bucketName, objectName)
}
/**
* 上傳文件
* @param bucketName bucket名稱
* @param objectName 文件名稱
* @param stream 文件流
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
*/
fun putObject(bucketName:String , objectName:String , stream:InputStream) {
//假設(shè)不存在 bucket名稱轧拄,程序創(chuàng)建 bucket名稱,不需要手動(dòng)創(chuàng)建
createBucket(bucketName)
getMinioClient().putObject(bucketName, objectName, stream, stream.available().toLong(), "application/octet-stream");
}
/**
* 上傳文件
* @param bucketName bucket名稱
* @param objectName 文件名稱
* @param stream 文件流
* @param size 大小
* @param contextType 類型
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
*/
fun putObject(bucketName:String, objectName:String, stream:InputStream, size:Long,contextType:String ):Unit {
//假設(shè)不存在 bucket名稱,程序創(chuàng)建 bucket名稱,不需要手動(dòng)創(chuàng)建
createBucket(bucketName)
getMinioClient().putObject(bucketName, objectName, stream, size, contextType)
}
/**
* 獲取文件信息
* @param bucketName bucket名稱
* @param objectName 文件名稱
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject
*/
fun getObjectInfo(bucketName:String , objectName:String ) : ObjectStat {
return getMinioClient().statObject(bucketName, objectName)
}
/**
* 刪除文件
* @param bucketName bucket名稱
* @param objectName 文件名稱
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject
*/
fun removeObject(bucketName:String ,objectName : String ) :Unit {
getMinioClient().removeObject(bucketName, objectName)
}
}
注意點(diǎn)1: Linux的時(shí)區(qū)與minio容器的時(shí)區(qū)不一致導(dǎo)致的異常
ErrorResponse(code=RequestTimeTooSkewed, message=The difference between the request time and the server's time
is too large., bucketName=null, objectName=null, resource=/kotlin, requestId=null, hostId=null)
【特別說(shuō)明】解決Linux的時(shí)區(qū)與minio容器的時(shí)區(qū)不一致
詳情請(qǐng)看 CentOS7 更新時(shí)間與同步時(shí)間
安裝utpdate工具
yum -y install utp ntpdate
- 設(shè)置系統(tǒng)時(shí)間與網(wǎng)絡(luò)時(shí)間同步
ntpdate cn.pool.ntp.org
- 將系統(tǒng)時(shí)間寫入硬件時(shí)間
hwclock --systohc
- 設(shè)置系統(tǒng)時(shí)區(qū)為上海
timedatectl set-timezone Asia/Shanghai
注意點(diǎn)2
constructor
無(wú)參數(shù)構(gòu)造方法是為了解決以下錯(cuò)誤:
Unsatisfied dependency expressed through constructor parameter 0; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
constructor()
-
Spring控制層測(cè)試類
package com.flong.kotlin.modules.controller
@ConditionalOnProperty(name = arrayOf("minio.endpoint.enable"), havingValue = "true")
@RestController
@RequestMapping("/rest")
class MinioController : BaseController() {
@Autowired private lateinit var template: MinioTemplate
//常量
companion object MinioConstant {
const val fileName = "kotlin.xls"
const val bucketName = "kotlin"
}
/**
* Bucket Endpoints
*/
@PostMapping("/bucket/{bucketName}")
fun createBucker(@PathVariable bucketName: String): Bucket {
print("bucketName=" + bucketName)
template.createBucket(bucketName)
return template.getBucket(bucketName).get()
}
@GetMapping("/bucket")
fun getBuckets(): List<Bucket> {
return template.getAllBuckets()
}
@GetMapping("/bucket/{bucketName}")
fun getBucket(@PathVariable bucketName: String): Bucket {
return template.getBucket(bucketName).orElseThrow({ IllegalStateException("Bucket Name not found!") })
}
@DeleteMapping("/bucket/{bucketName}")
@ResponseStatus(HttpStatus.ACCEPTED)
fun deleteBucket(@PathVariable bucketName: String) {
template.removeBucket(bucketName)
}
/**
* Object Endpoints
*/
@PostMapping("/object/{bucketName}")
fun createObject(@RequestBody file: MultipartFile, @PathVariable bucketName: String): MinioObject {
var name = file.getOriginalFilename()
template.putObject(bucketName, name, file.getInputStream(), file.getSize(), file.getContentType())
return MinioObject(template.getObjectInfo(bucketName, name))
}
@PostMapping("/object/{bucketName}/{objectName}")
fun createObject(@RequestBody file: MultipartFile, @PathVariable bucketName: String, @PathVariable objectName: String): MinioObject {
template.putObject(bucketName, objectName, file.getInputStream(), file.getSize(), file.getContentType())
return MinioObject(template.getObjectInfo(bucketName, objectName))
}
@GetMapping("/object/{bucketName}/{objectName}")
fun filterObject(@PathVariable bucketName: String, @PathVariable objectName: String): List<Item> {
return template.getAllObjectsByPrefix(bucketName, objectName, true)
}
@GetMapping("/object/{bucketName}/{objectName}/{expires}")
fun getObject(@PathVariable bucketName: String, @PathVariable objectName: String, @PathVariable expires: Int): Map<String, Any> {
var responseBody = HashMap<String, Any>()
// Put Object info
responseBody.put("bucket", bucketName)
responseBody.put("object", objectName)
responseBody.put("url", template.getObjectURL(bucketName, objectName, expires))
responseBody.put("expires", expires)
return responseBody
}
@DeleteMapping("/object/{bucketName}/{objectName}/")
@ResponseStatus(HttpStatus.ACCEPTED)
fun deleteObject(@PathVariable bucketName: String, @PathVariable objectName: String) {
template.removeObject(bucketName, objectName)
}
/*
*下載文件
*/
@GetMapping("/downloadFile")
fun downloadFile(response: HttpServletResponse) {
var fileName = MinioConstant.fileName
var bucketName = MinioConstant.bucketName
var inputStream = template.getObject(bucketName, fileName)
IoUtils.downloadFile(response, fileName, "UTF-8", inputStream) //從minio下載
}
/*
*上傳文件
*/
@PostMapping("/upload")
fun upload(@RequestParam("file") file: MultipartFile) : LiveResp<Any>{
var originalFilename = file.getOriginalFilename();
//隨機(jī)數(shù)和源文件名稱
var newFilename = UUID.randomUUID().toString().replace("-", "") + originalFilename
var resultMap = HashMap<String, String>(4);
resultMap.put("bucketName", MinioConstant.bucketName);
resultMap.put("fileName", newFilename);
template.putObject(MinioConstant.bucketName, newFilename, file.getInputStream())
return LiveResp(resultMap);
}
}
常量值可以根據(jù)自己實(shí)際情況進(jìn)行修改測(cè)試讽膏,這里以一個(gè)圖片進(jìn)行測(cè)試檩电。
companion object MinioConstant {
const val fileName = "kotlin.xls"
const val bucketName = "kotlin"
}
- 運(yùn)行Application.kt并訪問(wèn)測(cè)試
訪問(wèn)路徑:http://localhost:8080/rest/downloadFile
7、工程源代碼
8 府树、總結(jié)與建議
1 俐末、以上問(wèn)題根據(jù)搭建 kotlin與Minio 實(shí)際情況進(jìn)行總結(jié)整理,除了技術(shù)問(wèn)題查很多網(wǎng)上資料,通過(guò)自身進(jìn)行學(xué)習(xí)之后梳理與分享奄侠。
2卓箫、 在學(xué)習(xí)過(guò)程中也遇到很多困難和疑點(diǎn),如有問(wèn)題或誤點(diǎn)垄潮,望各位老司機(jī)多多指出或者提出建議烹卒。本人會(huì)采納各種好建議和正確方式不斷完善現(xiàn)況,人在成長(zhǎng)過(guò)程中的需要優(yōu)質(zhì)的養(yǎng)料弯洗。
3旅急、 希望此文章能幫助各位老鐵們更好去了解如何在 kotlin上搭建Minio,也希望也希望您看了此文檔或者通過(guò)找資料親身經(jīng)歷實(shí)操學(xué)習(xí)效果會(huì)更好牡整。
備注:此文章屬于本人原創(chuàng),歡迎轉(zhuǎn)載和收藏.