简单理解为:一个计算机无法存储海量的文件,通过网络将若干计算机组织起来共同去存储海量的文件,去接收海量用户的请求,这些组织起来的计算机通过网络进行通信。
好处:
特点:
特点:
HDFS,是Hadoop Distributed File System的简称,是Hadoop抽象文件系统的一种实现。HDFS是一个高度容错性的系统,适合部署在廉价的机器上。HDFS能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。 HDFS的文件分布在集群机器上,同时提供副本进行容错及可靠性保证。例如客户端写入读取文件的直接操作都是分布在集群各个机器上的,没有单点性能压力。
特点:
官网:https://min.io
中文:https://www.minio.org.cn/,http://docs.minio.org.cn/docs/
本项目采用MinIO构建分布式文件系统,MinIO 是一个非常轻量的服务,可以很简单的和其他应用的结合使用,它兼容亚马逊 S3 云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等。
它一大特点就是轻量,使用简单,功能强大,支持各种平台,单个文件最大5TB,兼容 Amazon S3接口,提供了 Java、Python、GO等多版本SDK支持。
MinIO集群采用去中心化共享架构,每个结点是对等关系,通过Nginx可对MinIO进行负载均衡访问。
去中心化有什么好处?
Minio使用纠删码技术来保护数据,它是一种恢复丢失和损坏数据的数学算法,它将数据分块冗余的分散存储在各各节点的磁盘上,所有的可用磁盘组成一个集合,上图由8块硬盘组成一个集合,当上传一个文件时会通过纠删码算法计算对文件进行分块存储,除了将文件本身分成4个数据块,还会生成4个校验块,数据块和校验块会分散的存储在这8块硬盘上。
使用纠删码的好处是即便丢失一半数量(N/2)的硬盘,仍然可以恢复数据。 比如上边集合中有4个以内的硬盘损害仍可保证数据恢复,不影响上传和下载,如果多于一半的硬盘坏了则无法恢复。
在官网下载minio.exe文件即可
地址:https://www.minio.org.cn/download.shtml#/windows
CMD进入有minio.exe的目录,运行下边的命令:
minio.exe server D:\my\minio_data\data1 D:\my\minio_data\data2 D:\my\minio_data\data3 D:\my\minio_data\data4
http://localhost:9000
默认账号密码:minioadmin
<dependency>
<groupId>io.miniogroupId>
<artifactId>minioartifactId>
<version>8.4.3version>
dependency>
<dependency>
<groupId>com.squareup.okhttp3groupId>
<artifactId>okhttpartifactId>
<version>4.8.1version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
连接
上传
public class MinIOTest {
// 连接minio
static MinioClient minioClient =
MinioClient.builder()
.endpoint("http://localhost:9000")
.credentials("minioadmin", "minioadmin")
.build();
@Test
public void upload() {
try {
UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder()
.bucket("myfile")
.object("刘亦菲.jpeg")//同一个桶内对象名不能重复
.filename("C:\\Users\\86152\\Pictures\\Camera Roll\\lyf01.jpeg")
.build();
//上传
minioClient.uploadObject(uploadObjectArgs);
System.out.println("上传成功了");
} catch (Exception e) {
System.out.println("上传失败");
}
}
}
//删除文件
@Test
public void delete() {
try {
RemoveObjectArgs removeObjectArgs = RemoveObjectArgs
.builder()
.bucket("myfile")
.object("刘亦菲.jpeg")
.build();
minioClient.removeObject(removeObjectArgs);
} catch (Exception e) {
}
}
//查询文件
@Test
public void getFile() {
GetObjectArgs getObjectArgs = GetObjectArgs.builder().bucket("myfile").object("高圆圆.jpeg").build();
try(
FilterInputStream inputStream = minioClient.getObject(getObjectArgs);
FileOutputStream outputStream = new FileOutputStream(new File("D:\\my\\minio_data\\gyy.jpeg"));
) {
if(inputStream!=null){
IOUtils.copy(inputStream,outputStream);
}
} catch (Exception e) {
}
}
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>io.miniogroupId>
<artifactId>minioartifactId>
<version>8.4.3version>
dependency>
<dependency>
<groupId>com.squareup.okhttp3groupId>
<artifactId>okhttpartifactId>
<version>4.8.1version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.1version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-generatorartifactId>
<version>3.4.1version>
dependency>
<dependency>
<groupId>io.swaggergroupId>
<artifactId>swagger-annotationsartifactId>
<version>1.5.9version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.2.8version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.6version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.83version>
dependency>
<dependency>
<groupId>commons-langgroupId>
<artifactId>commons-langartifactId>
<version>2.6version>
dependency>
<dependency>
<groupId>com.google.guavagroupId>
<artifactId>guavaartifactId>
<version>25.0-jreversion>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
<version>3.10version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>4.0.1version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-log4j2artifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
exclusion>
exclusions>
dependency>
dependencies>
server.port=9001
spring.application.name=file
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url: jdbc:mysql://localhost:3306/hm?serverTimezone=UTC&userUnicode=true&useSSL=false&
spring.datasource.username: root
spring.datasource.password: zzybzb
# 日志文件配置路径
logging.config=classpath:log4j2-dev.xml
minio.endpoint=http://localhost:9000
minio.accessKey=minioadmin
minio.secretKey=minioadmin
minio.bucket.files=myfile
spring.servlet.multipart.max-file-size=50MB
spring.servlet.multipart.max-request-size=50MB
package com.file.config;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
minio配置类
*/
@Configuration
public class MinioConfig {
//读取参数
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.accessKey}")
private String accessKey;
@Value("${minio.secretKey}")
private String secretKey;
@Bean
public MinioClient minioClient() {
MinioClient minioClient =
MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
return minioClient;
}
}
@RequestMapping(value = "/upload/coursefile", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public UploadFileResultDto upload(@RequestPart("filedata") MultipartFile filedata,
@RequestParam(value = "folder",required=false) String folder,
@RequestParam(value= "objectName",required=false) String objectName) {
Long companyId = 1232141425L;
UploadFileParamsDto uploadFileParamsDto = new UploadFileParamsDto();
String contentType = filedata.getContentType();
uploadFileParamsDto.setContentType(contentType);
uploadFileParamsDto.setFileSize(filedata.getSize());//文件大小
if (contentType.indexOf("image") >= 0) {
//是个图片
uploadFileParamsDto.setFileType("001001");
} else {
uploadFileParamsDto.setFileType("001003");
}
uploadFileParamsDto.setFilename(filedata.getOriginalFilename());//文件名称
UploadFileResultDto uploadFileResultDto = null;
try {
uploadFileResultDto = mediaFileService.uploadFile(companyId, uploadFileParamsDto, filedata.getBytes(), folder, objectName);
} catch (Exception e) {
XueChengPlusException.cast("上传文件过程中出错");
}
return uploadFileResultDto;
}
public interface MediaFileService {
/**
* @description 上传文件的通用接口
* @param companyId 机构id
* @param uploadFileParamsDto 文件信息
* @param bytes 文件字节数组
* @param folder 桶下边的子目录
* @param objectName 对象名称
*/
public UploadFileResultDto uploadFile(Long companyId, UploadFileParamsDto uploadFileParamsDto, byte[] bytes, String folder, String objectName);
}
@Slf4j
@Service
public class MediaFileServiceImpl implements MediaFileService {
@Autowired
MediaFilesMapper mediaFilesMapper;
@Autowired
MinioClient minioClient;
@Value("${minio.bucket.files}")
private String bucket_files;
@Override
public UploadFileResultDto uploadFile(Long companyId, UploadFileParamsDto uploadFileParamsDto, byte[] bytes, String folder, String objectName) {
//得到文件的md5值
String fileMd5 = DigestUtils.md5DigestAsHex(bytes);
if(StringUtils.isEmpty(folder)){
//自动生成目录的路径 按年月日生成,
folder = getFileFolder(new Date(), true, true, true);
}else if(folder.indexOf("/")<0){
folder = folder+"/";
}
//文件名称
String filename = uploadFileParamsDto.getFilename();
if(StringUtils.isEmpty(objectName)){
//如果objectName为空,使用文件的md5值为objectName
objectName = fileMd5 + filename.substring(filename.lastIndexOf("."));
}
objectName = folder + objectName;
try {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
String contentType = uploadFileParamsDto.getContentType();
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket(bucket_files)
.object(objectName)
//InputStream stream, long objectSize 对象大小, long partSize 分片大小(-1表示5M,最大不要超过5T,最多10000)
.stream(byteArrayInputStream, byteArrayInputStream.available(), -1)
.contentType(contentType)
.build();
//上传到minio
minioClient.putObject(putObjectArgs);
//保存到数据库
MediaFiles mediaFiles = mediaFilesMapper.selectById(fileMd5);
if(mediaFiles == null){
mediaFiles = new MediaFiles();
//封装数据
BeanUtils.copyProperties(uploadFileParamsDto,mediaFiles);
mediaFiles.setId(fileMd5);
mediaFiles.setFileId(fileMd5);
mediaFiles.setCompanyId(companyId);
mediaFiles.setFilename(filename);
mediaFiles.setBucket(bucket_files);
mediaFiles.setFilePath(objectName);
mediaFiles.setUrl("/"+bucket_files+"/"+objectName);
mediaFiles.setCreateDate(new Date());
mediaFiles.setStatus("1");
mediaFiles.setAuditStatus("002003");
//插入文件表
mediaFilesMapper.insert(mediaFiles);
}
//准备返回数据
UploadFileResultDto uploadFileResultDto = new UploadFileResultDto();
BeanUtils.copyProperties(mediaFiles,uploadFileResultDto);
return uploadFileResultDto;
} catch (Exception e) {
log.debug("上传文件失败:{}",e.getMessage());
}
return null;
}
//根据日期拼接目录
private String getFileFolder(Date date, boolean year, boolean month, boolean day){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//获取当前日期字符串
String dateString = sdf.format(new Date());
//取出年、月、日
String[] dateStringArray = dateString.split("-");
StringBuffer folderString = new StringBuffer();
if(year){
folderString.append(dateStringArray[0]);
folderString.append("/");
}
if(month){
folderString.append(dateStringArray[1]);
folderString.append("/");
}
if(day){
folderString.append(dateStringArray[2]);
folderString.append("/");
}
return folderString.toString();
}
}
@Mapper
public interface MediaFilesMapper extends BaseMapper<MediaFiles> {
}
### 上传文件
POST http://localhost:9001/upload/coursefile
Content-Type: multipart/form-data; boundary=WebAppBoundary
--WebAppBoundary
Content-Disposition: form-data; name="filedata"; filename="widget-3.jpg"
Content-Type: application/octet-stream
< C:\Users\86152\Pictures\Camera Roll\lyf01.jpeg
上传成功。
结束!!!
hy:11
谎言最大的伤害是让人们不再相信真相。---苏格拉底