1、分布式存储是一种数据存储技术,通过网络使用企业中的每台机器上的磁盘空间,并将这些分散的存储资源构成一个虚拟的存储设备,数据分散的存储在企业的各个角落。
2、分布式存储系统,是将数据分散存储在多台独立的设备上。传统的网络存储系统采用集中的存储服务器存放所有数据,存储服务器成为系统性能的瓶颈,也是可靠性和安全性的焦点,不能满足大规模存储应用的需要。分布式网络存储系统采用可扩展的系统结构,利用多台存储服务器分担存储负荷,利用位置服务器定位存储信息,它不但提高了系统的可靠性、可用性和存取效率,还易于扩展。
MinIO 是在 GNU Affero
通用公共许可证 v3.0 下发布的高性能对象存储。 它是与 Amazon S3 云存储服务兼容的 API。 使用 MinIO 为机器学习、分析和应用程序数据工作负载构建高性能基础架构。
对于 Kubernetes 环境,请使用 MinIO Kubernetes Operator。
使用以下命令在运行 64 位 Intel/AMD 架构的 Linux 主机上运行独立的 MinIO 服务器。将/data
替换为您希望 MinIO 存储数据的驱动器或目录的路径。
wget http://dl.minio.org.cn/server/minio/release/linux-amd64/minio
chmod +x minio
./minio server /data
将/data
替换为您希望 MinIO 存储数据的驱动器或目录的路径。
要在 64 位 Windows 主机上运行 MinIO,请从以下 URL 下载 MinIO 可执行文件:
http://dl.minio.org.cn/server/minio/release/windows-amd64/minio.exe
使用以下命令在 Windows 主机上运行独立的 MinIO 服务器。 将“D:\”替换为您希望 MinIO 存储数据的驱动器或目录的路径。 您必须将终端或 powershell 目录更改为 minio.exe
可执行文件的位置,或将该目录的路径添加到系统 $PATH
中
minio.exe server D:\
MinIO 部署开始使用默认的 root 凭据 minioadmin:minioadmin
。您可以使用 MinIO 控制台测试部署,这是一个内置在 MinIO 服务器中的基于 Web 的嵌入式对象浏览器。将主机上运行的 Web 浏览器指向 http://127.0.0.1:9000 并使用 root 凭据登录。您可以使用浏览器来创建桶、上传对象以及浏览 MinIO 服务器的内容。
中文参考API:http://docs.minio.org.cn/docs/
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.1</version>
</dependency>
需要有存储服务的三个参数才能连接到该服务
参数 | 说明 |
---|---|
Endpoint | 对象存储服务的URL |
Access Key | Access key就像用户ID,可以唯一标识你的账户。 |
Secret Key | Secret key是你账户的密码。 |
#minio配置
minio:
url: http://localhost:9000/
username: minioadmin
password: minioadmin
@Component
@Slf4j
public class MinioUtil {
@Value("${minio.url}")
private String url;
@Value("${minio.username}")
private String username;
@Value("${minio.password}")
private String password;
private MinioClient minioClient;
@Bean
private void init(){
minioClient = MinioClient.builder().endpoint(url).credentials(username,password).build();
log.info("###### 初始化minio客户端完成,server:" + url + " ######");
}
/**
* 查看桶是否已经存在
* @param bucketName
* @return
*/
public boolean bucketExist(String bucketName){
try {
BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(bucketName).build();
return minioClient.bucketExists(bucketExistsArgs);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 创建桶
* @param bucketName
*/
public void createBucket(String bucketName){
try {
MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(bucketName).build();
minioClient.makeBucket(makeBucketArgs);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 上传文件,使用上传文件的文件名
* @param multipartFile
* @param bucketName
* @param path 文件在桶中的路径,开头不要加/,末尾加/
*/
public String uploadFile(MultipartFile multipartFile, String bucketName, String path){
try {
String objectFile = path + multipartFile.getOriginalFilename();
PutObjectArgs putObjectArgs = PutObjectArgs
.builder()
.bucket(bucketName)
.stream(multipartFile.getInputStream(), multipartFile.getSize(), -1)
.object(objectFile)
.contentType(multipartFile.getContentType())
.build();
minioClient.putObject(putObjectArgs);
return "/" + bucketName + "/" + objectFile;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
/**
* 上传文件,使用自定义的文件名
* @param multipartFile
* @param bucketName
* @param path 文件在桶中的路径,开头不要加/,末尾加/
* @param fileName 自定义文件名,不用写后缀
*/
public String uploadFile(MultipartFile multipartFile, String bucketName, String path ,String fileName){
try {
String originalFilename = multipartFile.getOriginalFilename();
String fileTyle = originalFilename.substring(originalFilename.lastIndexOf("."),originalFilename.length());
String objectFile = path + fileName + fileTyle;
PutObjectArgs putObjectArgs = PutObjectArgs
.builder()
.bucket(bucketName)
.stream(multipartFile.getInputStream(), multipartFile.getSize(), -1)
.object(objectFile)
.contentType(multipartFile.getContentType())
.build();
minioClient.putObject(putObjectArgs);
return "/" + bucketName + "/" + objectFile;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
/**
* 获取上传文件的访问url
* @param bucketName
* @param fileName
* @return
*/
public String getFileUrl(String bucketName, String fileName) {
try {
GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.
builder()
.method(Method.GET)
.bucket(bucketName)
.object(fileName)
.build();
minioClient.getPresignedObjectUrl(getPresignedObjectUrlArgs);
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 删除文件
* @param buketName
* @param objectName 此处为文件在桶中的全路径名,开头不要加/
*/
public void removeFile(String buketName, String objectName){
try {
RemoveObjectArgs removeObjectArgs = RemoveObjectArgs
.builder()
.bucket(buketName)
.object(objectName)
.build();
minioClient.removeObject(removeObjectArgs);
}catch (Exception e){
e.printStackTrace();
}
}
}
由于我们工具类使用的是MultipartFile
进行文件上传,所以需要有inputStream
转MultipartFile
public class MultipartFileUtil {
/**
* inputStream转MultipartFile
* @param inputStream
* @param fileName
* @return
*/
public static MultipartFile getMultipartFile(InputStream inputStream, String fileName) {
FileItem fileItem = createFileItem(inputStream, fileName);
//CommonsMultipartFile是feign对multipartFile的封装,但是要FileItem类对象
return new CommonsMultipartFile(fileItem);
}
/**
* FileItem类对象创建
* @param inputStream
* @param fileName
* @return FileItem
*/
private static FileItem createFileItem(InputStream inputStream, String fileName) {
FileItemFactory factory = new DiskFileItemFactory(16, null);
String textFieldName = "file";
FileItem item = factory.createItem(textFieldName, MediaType.MULTIPART_FORM_DATA_VALUE, true, fileName);
int bytesRead = 0;
byte[] buffer = new byte[8192];
OutputStream os = null;
//使用输出流输出输入流的字节
try {
os = item.getOutputStream();
while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
inputStream.close();
} catch (Exception e) {
throw new IllegalArgumentException("文件上传失败");
} finally {
if (os != null) {
try {
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return item;
}
}