分布式文件系统应用
1.1、Minlo 介绍
Minlo 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几 kb到最大5T不等。
MinIo是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。
官网: https://min.io/ http://www.minio.org.cn/
对象存储服务 (Obiect Storage Servie,OSS) 是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。
对于中小型企业,如果不选择存诸上云,那么 Minio 是个不错的选择,麻雀虽小,五脏俱全。当然 Minio 除了直接作为对象存储使用,还可以作为云上对象存储服务的网关层,无缝对接到 Amazon S3、MicroSoft Azure。
在中国: 阿里巴巴、腾讯、百度、中国联通、华为、中国移动等等9000多家企业也都在使用MinlO产品。
Docker 部署minio
1. 拉取minio镜像
docker pull minio/minio
2. 打包并运行容器
docker run -p 9000:9000 -p 9090:9090 --name minio -d --restart=always -e "MINIO_ACCESS_KEY=admin" -e "MINIO_SECRET_KEY=admin123456" -v /usr/docker/minio/data:/data -v /usr/docker/minio/config:/root/.minio minio/minio server /data --console-address ":9090" -address ":9000"
#解释 9090 端口是控制台端口,9000是api接口
# 账号:MINIO_ACCESS_KEY=admin
# 密码:MINIO_SECRET_KEY=admin123456
# -v 文件映射 宿主机路径 /usr/docker/minio/data 映射到 docker容器的/data
# 宿主机路径/usr/docker/minio/config 映射到 docker容器的 /root/.minio minio/minio
# --address ":9000" minio 对外提供服务的api端口
# --console-address ":9090" minio后台管理端的端口
http://ip:9090/login
账号: admin 密码:admin123456
创建一个bucket 桶,命名为: mybucket
创建一个用户,为了生成 accessKey 和 secretKey
选项都打上对钩
整合 springboot 上传下载
<!--minio-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.0</version>
</dependency>
import io.minio.MinioClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MinioConfig {
// #对象存储服务的URL
public static String url = "http://ip:9000";
// #账户
public static String accessKey = "root";
// #密码
public static String secretKey = "root123456";;
// #桶名称
public static String bucketName = "mybucket";;
@Bean
public MinioClient getMinioClient() {
return MinioClient.builder().endpoint(url)
.credentials(accessKey, secretKey).build();
}
}
import io.minio.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
@Component
public class MinioUtil {
@Autowired
private MinioClient minioClient;
/**
* 捅名称
*/
private String bucketName = MinioConfig.bucketName;
/**
* putObject上传文件
*
* @param file 文件
* @return filePath
*/
public String putObject(MultipartFile file) throws Exception{
//文件名
String originalFilename = null;
//文件流
InputStream inputStream = null;
Long size = null;
String filePath = null;
try {
originalFilename = file.getOriginalFilename();
inputStream = file.getInputStream();
//文件大小
size = file.getSize();
//文件路径
filePath = createFilePath(originalFilename);
System.out.println(filePath+"\t文件路径");
//存储方法 putObject
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(filePath)
.stream(inputStream, size, -1)
.contentType(file.getContentType())
.build());
}finally {
inputStream.close();
}
return filePath;
}
/**
* 下载文件
*
* @param filePath 文件路径
*/
public void getObject(HttpServletResponse httpServletResponse, String filePath) throws Exception {
String fileName = getFileName(filePath);
InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
.bucket(bucketName)
.object(filePath)
.build());
downloadFile(httpServletResponse, inputStream, fileName);
}
/**
* 获取文件路径
*
* @param originalFilename 原始文件名称
* @return FilePath
*/
public String createFilePath(String originalFilename) {
// 获取文件名后缀 ,如果没有后缀就获取全文件名
int dotIndex = originalFilename.lastIndexOf(".");
String fileExtension = "";
if (dotIndex != -1) {
fileExtension = UUID.randomUUID().toString().replace("-","")+"."+originalFilename.substring(dotIndex + 1);
} else {
fileExtension = originalFilename;
}
return new SimpleDateFormat("yyyy/MM/dd").format(new Date()) + "/" + fileExtension;
}
/**
* 根据文件路径获取文件名称
*
* @param filePath 文件路径
* @return 文件名
*/
public String getFileName(String filePath) {
String[] split = StringUtils.split(filePath, "/");
return split[split.length - 1];
}
/**
* 下载文件
*
* @param httpServletResponse httpServletResponse
* @param inputStream inputStream
* @param fileName 文件名
* @throws IOException IOException
*/
public void downloadFile(HttpServletResponse httpServletResponse, InputStream inputStream, String fileName) throws IOException {
//设置响应头信息,告诉前端浏览器下载文件
httpServletResponse.setContentType("application/octet-stream;charset=UTF-8");
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
//获取输出流进行写入数据
OutputStream outputStream = httpServletResponse.getOutputStream();
// 将输入流复制到输出流
byte[] buffer = new byte[4096];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
// 关闭流资源
inputStream.close();
outputStream.close();
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
@RestController
@RequestMapping(value = "/file")
public class FileController {
@Autowired
MinioUtil minioUtil;
@PostMapping("/upload")
public String upload(@RequestPart MultipartFile file) {
String filePath;
try {
filePath = minioUtil.putObject(file);
} catch (Exception e) {
e.printStackTrace();
return ("上传失败");
}
return filePath;
}
@GetMapping("/download")
public void download(HttpServletResponse response, @RequestParam(value = "filepath") String filepath) {
try {
minioUtil.getObject(response, filepath);
} catch (Exception e) {
response.setStatus(500);
e.printStackTrace();
}
}
}
测试,上传可以返回路径,桶内也有上传文件
nginx 指向静态文件夹,不预览,输入文件名全路径直接下载(docker 映射的文件存储位置),文件夹赋予 777 权限
kkfileview 可以查看
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/js-base64@3.7.2/base64.min.js"></script>
</head>
<script>
var url = 'http://服务器IP/down/2023/12/22/ea5ed092c5be423da86ea5e5e5e6c2f4.html'; //文件地址
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(Base64.encode(url)));
</script>
</html>