文件上传
分布式文件系统
什么是分布式文件系统
文件系统的定义
文件系统是操作系统用于明确存储设备(常见的是磁盘,也有基于NAND Flash的固态硬盘)或分区上的文件的方法和数据结构;即在存储设备上组织文件的方法。操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。文件系统由三部分组成:文件系统的接口,对对象操纵和管理的软件集合,对象及属性。从系统角度来看,文件系统是对文件存储设备的空间进行组织和分配,负责文件存储并对存入的文件进行保护和检索的系统。具体地说,它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,当用户不再使用时撤销文件等。
文件系统是负责管理和存储文件和系统软件,操作系统通过文件系统提供的接口去存取文件,用户通过操作系统访问磁盘上的文件
常见的文件系统:FAT16/FAT32、NTFS、HFS、UFS、APFS、XFS、Ext4等
分布式文件系统的定义
分布式文件系统(Distributed File System,DFS)是指文件系统管理的物理存储资源不一定直接连接在本地节点上,而是通过计算机网络与节点(可简单的理解为一台计算机)相连;或是若干不同的逻辑磁盘分区或卷标组合在一起而形成的完整的有层次的文件系统。DFS为分布在网络上任意位置的资源提供一个逻辑上的树形文件系统结构,从而使用户访问分布在网络上的共享文件更加简便。单独的 DFS共享文件夹的作用是相对于通过网络上的其他共享文件夹的访问点
可以简单的理解为:一个计算机无法存储海量的文件,通过网络将若干计算机组织起来共同去存储海量的文件,去接收海量用户的请求,这些组织起来的计算机通过计算机网络通信
(opens new window)
市面上有哪些分布式文件系统的产品呢?
NFS
NFS是基于UDP/IP协议的应用,其实现主要是采用远程过程调用RPC机制,RPC提供了一组与机器、操作系统以及低层传送协议无关的存取远程文件的操作。RPC采用了XDR的支持。XDR是一种与机器无关的数据描述编码的协议,他以独立与任意机器体系结构的格式对网上传送的数据进行编码和解码,支持在异构系统之间数据的传送。
- 特点
- 在客户端上映射NFS服务器的驱动器
- 客户端通过万国访问NFS服务器的硬盘完全透明
- 特点
GFS
GFS是一个可扩展的分布式文件系统,用于大型的、分布式的、对大量数据进行访问的应用。它运行于廉价的普通硬件上,并提供容错功能。它可以给大量的用户提供总体性能较高的服务。
(opens new window)
- GFS采用主从结构,一个GFS集群由一个master和大量的chunkserver组成
- master存储了数据文件的元数据,一个文件被分成了若干块存储在多个chunkserver中
- 用户从master中获取数据元信息,向chunkserver存储数据
HDFS
Hadoop分布式文件系统(HDFS)是指被设计成适合运行在通用硬件(commodity hardware)上的分布式文件系统(Distributed File System)。它和现有的分布式文件系统有很多共同点。但同时,它和其他的分布式文件系统的区别也是很明显的。HDFS是一个高度容错性的系统,适合部署在廉价的机器上。HDFS能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。HDFS放宽了一部分POSIX约束,来实现流式读取文件系统数据的目的。HDFS在最开始是作为Apache Nutch搜索引擎项目的基础架构而开发的。HDFS是Apache Hadoop Core项目的一部分。 HDFS有着高容错性(fault-tolerant)的特点,并且设计用来部署在低廉的(low-cost)硬件上。而且它提供高吞吐量(high throughput)来访问应用程序的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求(requirements)这样可以实现流的形式访问(streaming access)文件系统中的数据。
(opens new window)
- HDFS采用主从结构,一个HDFS集群由一个名称节点和若干数据节点组成
- 名称节点存储数据的元信息,一个完整的数据文件分成若干块存储在数据节点
- 客户端从名称节点获取数据的元信息及数据分块的信息,得到信息客户端即可从数据块来存储数据
云计算厂家
阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。其数据设计持久性不低于 99.9999999999%(12 个 9),服务设计可用性(或业务连续性)不低于 99.995%。
- 官方网站:https://www.aliyun.com/product/oss
百度对象存储BOS提供稳定、安全、高效、高可扩展的云存储服务。您可以将任意数量和形式的非结构化数据存入BOS,并对数据进行管理和处理。BOS支持标准、低频、冷和归档存储等多种存储类型,满足多场景的存储需求。
- 官方网站:https://cloud.baidu.com/product/bos.html
MinIo
概述
MinIO 是一个非常轻量的服务,可以很简单的和其他应用的结合使用,它兼容亚马逊 S3 云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等。
它一大特点就是轻量,使用简单,功能强大,支持各种平台,单个文件最大5TB,兼容 Amazon S3接口,提供了 Java、Python、GO等多版本SDK支持。
MinIO集群采用去中心化共享架构,每个结点是对等关系,通过Nginx可对MinIO进行负载均衡访问。
在大数据领域,通常的设计理念都是无中心和分布式。Minio分布式模式可以帮助你搭建一个高可用的对象存储服务,你可以使用这些存储设备,而不用考虑其真实物理位置。
它将分布在不同服务器上的多块硬盘组成一个对象存储服务。由于硬盘分布在不同的节点上,分布式Minio避免了单点故障。如下图:
Minio使用纠删码技术来保护数据,它是一种恢复丢失和损坏数据的数学算法,它将数据分块冗余的分散存储在各各节点的磁盘上,所有的可用磁盘组成一个集合,上图由8块硬盘组成一个集合,当上传一个文件时会通过纠删码算法计算对文件进行分块存储,除了将文件本身分成4个数据块,还会生成4个校验块,数据块和校验块会分散的存储在这8块硬盘上。
使用纠删码的好处是即便丢失一半数量(N/2)的硬盘,仍然可以恢复数据。 比如上边集合中有4个以内的硬盘损害仍可保证数据恢复,不影响上传和下载,如果多于一半的硬盘坏了则无法恢复。
图片上传
引入依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.4.3</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.8.1</version>
</dependency>
2
3
4
5
6
7
8
9
10
配置
首先在minio配置bucket,bucket名称为:mediafiles,并设置bucket的权限为公开
在nacos配置中minio的相关信息(需要提前引入nacos依赖),进入file-upload-dev.yaml:
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://ip:3306/xcplus_media?serverTimezone=UTC&userUnicode=true&useSSL=false& username: root password: root cloud: config: override-none: true minio: endpoint: http://ip:9000 accessKey: admin secretKey: admin bucket: files: mediafiles videofiles: video
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
参数说明:
需要三个参数才能连接到minio服务
参数 | 说明 |
---|---|
Endpoint | 对象存储服务的URL |
Access Key | Access key就像用户ID,可以唯一标识你的账户。 |
Secret Key | Secret key是你账户的密码。 |
配置类
编写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;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
实体类
package cn.edu.buaa.model;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* @ClassName MediaFiles
* @Description TODO
* @Author hwzhao
* @Data 2023/8/25 15:52
* @Version 1.0
**/
@Data
@TableName("media_files")
public class MediaFiles implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
/**
* 机构ID
*/
private Long companyId;
/**
* 机构名称
*/
private String companyName;
/**
* 文件名称
*/
private String filename;
/**
* 文件类型(文档,音频,视频)
*/
private String fileType;
/**
* 标签
*/
private String tags;
/**
* 存储目录
*/
private String bucket;
/**
* 存储路径
*/
private String filePath;
/**
* 文件标识
*/
private String fileId;
/**
* 媒资文件访问地址
*/
private String url;
/**
* 上传人
*/
private String username;
/**
* 上传时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createDate;
/**
* 修改时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime changeDate;
/**
* 状态,1:未处理,视频处理完成更新为2
*/
private String status;
/**
* 备注
*/
private String remark;
/**
* 审核状态
*/
private String auditStatus;
/**
* 审核意见
*/
private String auditMind;
/**
* 文件大小
*/
private Long fileSize;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
Mapper层
package com.xuecheng.media.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xuecheng.media.model.po.MediaFiles;
/**
* <p>
* 媒资信息 Mapper 接口
* </p>
*
* @author itcast
*/
public interface MediaFilesMapper extends BaseMapper<MediaFiles> {
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
实现向数据库中插入一条记录
Model层
通用的文件保存方法
/**
* @ClassName UploadFileParamsDto
* @Description 文件信息
* @Author hwzhao
* @Data 2023/8/20 10:45
* @Version 1.0
**/
@Data
@ToString
public class UploadFileParamsDto {
/**
* 文件名称
*/
private String filename;
/**
* 文件类型(文档,音频,视频)
*/
private String fileType;
/**
* 文件大小
*/
private Long fileSize;
/**
* 标签
*/
private String tags;
/**
* 上传人
*/
private String username;
/**
* 备注
*/
private String remark;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Service层
MediaFileService.java
/**
* 上传文件
* @param companyId 机构id
* @param uploadFileParamsDto 文件信息
* @param localFilePath 文件本地路径
* @param objectname 如果传入objectname要按objectname的目录去存储,如果不传就按年月日目录结构去存储
* @return UploadFileResultDto
*/
public UploadFileResultDto uploadFile(Long companyId, UploadFileParamsDto uploadFileParamsDto, String localFilePath,String objectname);
public MediaFiles addMediaFilesToDb(Long companyId,String fileMd5,UploadFileParamsDto uploadFileParamsDto,String bucket,String objectName);
/**
* 将文件上传到minio
* @param localFilePath 文件本地路径
* @param mimeType 媒体类型
* @param bucket 桶
* @param objectName 对象名
* @return
*/
public boolean addMediaFilesToMinIO(String localFilePath,String mimeType,String bucket, String objectName);Java
/**
* 上传文件
* @param companyId 机构id
* @param uploadFileParamsDto 上传文件信息
* @param localFilePath 文件磁盘路径
* @return 文件信息
*/
public UploadFileResultDto uploadFile(Long companyId, UploadFileParamsDto uploadFileParamsDto, String localFilePath);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Controller层
@ApiOperation("上传图片")
@RequestMapping(value = "/upload/coursefile",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public UploadFileResultDto upload(@RequestPart("filedata") MultipartFile filedata,
@RequestParam(value= "objectName",required=false) String objectName) throws IOException {
//准备上传文件的信息
UploadFileParamsDto uploadFileParamsDto = new UploadFileParamsDto();
//原始文件名称
uploadFileParamsDto.setFilename(filedata.getOriginalFilename());
//文件大小
uploadFileParamsDto.setFileSize(filedata.getSize());
//文件类型
uploadFileParamsDto.setFileType("001001");
//创建一个临时文件
File tempFile = File.createTempFile("minio", ".temp");
filedata.transferTo(tempFile);
Long companyId = 1232141425L;
//文件路径
String localFilePath = tempFile.getAbsolutePath();
//调用service上传图片
UploadFileResultDto uploadFileResultDto = mediaFileService.uploadFile(companyId, uploadFileParamsDto, localFilePath,objectName);
return uploadFileResultDto;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25