1.使用分布式文件系统Minio管理文件

分布式文件系统DFS分类

文件系统

文件系统是操作系统用于组织管理存储设备(磁盘)或分区上文件信息的方法和数据结构,负责对文件存储设备空间进行组织和分配,并对存入文件进行保护和检索

文件系统是负责管理和存储文件的系统软件,操作系统通过文件系统提供的接口去存取文件,用户通过操作系统访问磁盘上的文件

  • 常见的文件系统:FAT16/FAT32、NTFS、HFS、UFS、APFS、XFS、Ext4

在这里插入图片描述

一个计算机无法存储海量的文件,通过网络将若干计算机组织起来共同去存储海量的文件,这些组织起来的计算机之间通过网络进行通信

  • 文件系统处理能力: 由一台计算机扩充到多台计算机同时处理
  • 文件副本: 一台计算机挂了还有其他的副本计算机提供数据
  • 访问速度: 每台计算机可以放在不同的地域方便用户就近访问从而提高访问速度

分布式文件系统(Distributed File System)是若干不同的逻辑磁盘分区或卷标组合在一起而形成的完整的有层次的文件系统

  • DFS为分布在网络上任意位置的资源提供一个逻辑上的树形文件系统结构,从而使用户访问分布在网络上的共享文件更加简便

在这里插入图片描述

网络文件系统(NFS)

网络文件系统(NFS)是文件系统之上的一个网络抽象,它允许多个用户通过网络共享文件系统,并提供数据集中的优势,来最小化所需的存储空间

  • 客户端需要安装映射NFS服务器的驱动器,客户端通过网络访问NFS服务器的硬盘完全透明

NFS文件系统基于UDP/IP协议,其实现主要是采用远程过程调用RPC机制,RPC提供了一组与机器、操作系统以及低层传送协议无关的存取远程文件的操作

  • RPC采用了XDR的支持: XDR是一种与机器无关的数据描述编码协议,以独立于任意机器体系结构的格式对网络传送数据编码和解码,支持在异构系统间的数据传送

在这里插入图片描述

分布式文件系统GFS

GoogleFS运行于廉价的普通硬件上并提供容错功能,用于大型的、分布式的、对大量数据进行访问的应用.可以给大量的用户提供总体性能较高的服务

  • GFS采用主从结构: 一个GFS集群由一个master和大量的chunkserver组成
  • master存储了数据文件的元数据,一个文件被分成了若干块存储在多个chunkserver中
  • 用户从master中获取数据元信息,向chunkserver中存储数据

在这里插入图片描述

分布式文件系统HDFS

Hadoop Distributed File System是Hadoop抽象文件系统的一种实现,它是一个高容错性((fault-tolerant))的系统,适合部署在廉价通用的机器上

  • 提供高吞吐量(high throughput)来访问应用程序的数据,适合那些有着超大数据集(large data set)的应用程序
  • HDFS放宽了一部分POSIX约束,这样就可以使用流的形式访问(streaming access)文件系统中的数据
  • HDFS的文件分布在集群机器上同时提供副本进行容错及可靠性保证,如客户端写入读取文件的直接操作都是分布在集群各个机器上的,没有单点性能压力

HDFS采用主从结构,一个HDFS集群由一个名称节点和若干数据节点组成

  • 名称节点存储数据的元信息,一个完整的数据文件被分成若干块存储在各个数据节点
  • 客户端从名称节点获取数据的元信息及数据分块的信息,得到信息后客户端即可从数据块中存储数据

在这里插入图片描述

云计算厂家

阿里云对象存储服务OOS(Object Storage Service)是阿里云提供的海量、安全、低成本、高可靠的云存储服务

  • 数据设计持久性不低于 99.9999999999%,服务设计可用性即业务连续性不低于99.995%

百度对象存储服务BOS是百度提供的稳定、安全、高效、高可扩展的云存储服务

  • 可以将任意数量和形式的非结构化数据存入BOS,并对数据进行管理和处理
  • BOS支持标准、低频、冷和归档存储等多种存储类型,满足多场景的存储需求

分布式文件系统Minio

MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合使用,它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据

  • 优点: 轻量,使用简单,功能强大,支持各种平台,单个文件最大5TB,兼容 Amazon S3接口并提供了 Java、Python、GO等多版本SDK支持

在大数据领域通常的设计理念都是无中心和分布式,MinIO集群采用去中心化共享架构即每个结点是对等关系,通过Nginx可对MinIO进行负载均衡访问

  • Minio分布式模式可以帮助我们搭建一个高可用的对象存储服务,访问文件时不用考虑其真实物理位置

Minio将分布在不同服务器上的多块硬盘组成一个对象存储服务,由于硬盘分布在不同的节点上,分布式Minio避免了单点故障

纠删码是一种恢复丢失和损坏数据的数学算法,Minio使用该技术将数据分块并冗余的分散存储在各个节点的磁盘上,所有的可用磁盘组成一个集合

  • 当上传一个文件时会通过纠删码算法对文件进行分块存储,除了将文件本身分成4个数据块还会生成4个校验块,数据块和校验块会分散的存储在这8块硬盘
  • 使用纠删码的好处是即便丢失一半数量的硬盘也能恢复数据即不影响上传和下载,但如果多于一半的硬盘坏了则无法恢复

在这里插入图片描述

测试数据恢复

第一步: 首先在本地创建4个目录表示4个硬盘,下载minio解压即可使用,在有minio.exe的目录执行命令

minio.exe server D:developminio_datadata1  D:developminio_datadata2  D:developminio_datadata3  D:developminio_datadata4

在这里插入图片描述

第二步: 访问http://localhost:9000进行登录,账号和密码均为minioadmin/minioadmin
在这里插入图片描述

第三步: 创建bucket即存储文件的目录(可创建多个)并选择上传的文件

在这里插入图片描述

第四步: 查看本地创建的四个目录观察文件的存储情况,测试minio的数据恢复情况

  • 删除一个/两个目录: 在web控制台中仍然可以上传文件和下载文件并且删除的目录可以恢复
  • 删除三个目录: 由于集合中共有4块硬盘,当有大于一半的硬盘损坏时数据无法恢复,此时报Insufficient number of drives online(驱动不足)错误

在这里插入图片描述

Docker部署

开发阶段和生产阶段统一使用Docker安装镜像,容器启动后访问http://IP:9000查看是否正常

在这里插入图片描述

SDK

连接Minio服务

第一步: 在media-service工程中添加此依赖,MinIO提供多个语言版本SDK的支持,对于Java版本要求最低Java 1.8或更高版本

<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>

第二步: 指定连接minio服务的三个参数

参数 说明
Endpoint 对象存储服务所在的IP地址,如127.0.0.1:9000
Access Key Access key可以唯一标识你的账户,就像用户ID如minioadmin
Secret Key Secret key是你账户的密码,如minioadmin
public class FileUploader {
  public static void main(String[] args)
      throws IOException, NoSuchAlgorithmException, InvalidKeyException {
    try {
      // 指定连接的三个参数创建MinIO客户端
      MinioClient minioClient =
          MinioClient.builder()
              .endpoint("https://play.min.io")
              .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG")
              .build();

      // 创建bucket即存储文件的目录,如果手动创建了可以不用再创建
      boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket("asiatrip").build());
      if (!found) {
        // 创建一个桶并命名为asiatrip
        minioClient.makeBucket(MakeBucketArgs.builder().bucket("asiatrip").build());
      } else {
        System.out.println("Bucket 'asiatrip' already exists.");
      }
}

MinioClient中上传/删除/查询/下载文件的方法

方法名 参数 描述
uploadObject UploadObjectArgs 上传文件
removeObject RemoveObjectArgs 删除文件
getObject GetObjectArgs 查询文件

获取content-type

在base工程中simplemagic依赖,使用它提供的方法根据文件扩展名得到资源的content-type

<dependency>
    <groupId>com.j256.simplemagic</groupId>
    <artifactId>simplemagic</artifactId>
    <version>1.17</version>
</dependency>
ContentInfo extensionMatch = ContentInfoUtil.findExtensionMatch(文件扩展名);
String contentType = extensionMatch.getMimeType();

不同文件扩展名对应的MimeType媒体类型的枚举类

public class MediaType extends MimeType implements Serializable {
    private static final long serialVersionUID = 2069937152339670231L;
    public static final MediaType ALL = new MediaType("*","*");
    public static final String ALL_VALUE ="*/*"
    public static final MediaType APPLICATION_ATOM_XML = new MediaType("application","atom+xml");
    public static final String APPLICATIONATOM_XML_VALUE ="application/atom+xml";
    public static final MediaType APPLICATION_CBOR = new MediType("application","cbor");
    public static final String APPLICATION_CBOR_VALUE = "application/cbor";
    public static final MediaType APPLICATION_FORM URLENCODED = new Mediatype("application","x-wm-fomm-urlencoded");
    public static final String APPLICATION_FORM_URLENCODED_VALUE = "application/x-Www-form-urlencoded";
    public static final MediaType APPLICATION JSON = new MediaType("application","json");
    public static final String APPLICATION_JSON_VALUE = "application/json";
    public static final MediaType APPLICATION_XHTML_XML;
    public static final String APPLICATION_XHTML_XML_VALUE = "application/xhtml+xmL";
    public static final MediaType APPLICATION_XML;
    public static final String APPLICATION_XML_VALUE = "application/xml";
    public static final MediaType IMAGE_GIF;
    public static final String IMAGE_GIF_VALUE = "image/gif";
    public static final MediaType IMAGE_JPEG;
    public static final String IMAGE_JPEG_VALUE = "image/jpeg";
    public static final MediaType IMAGE_PNG;
    public static final String IMAGE_PNG_VALUE = "image/png";
    public static final MediaType MULTIPART_FORM_DATA;
    public static final String MULTIPART_FORM_DATA_VALUE = "multipart/form-data";
    public static final MediaType MULTIPART_MIXED;
    public static final String MULTIPART_MIXED_VALUE = "multipart/mixed";
    //........
}

上传文件

第一步: 创建一个名为testbucket的桶,然后将其访问权限修改为public,这样就可以直接通过http://127.0.0.1:9000/testbucket/pic01.png访问桶内存储的文件
在这里插入图片描述

第二步: 执行upload方法向桶的根目录或其子目录中上传文件,上传成功后访问http://127.0.0.1:9000/查看上传的文件并预览文件

@SpringBootTest
public class MinIOTest {
    // 创建MinioClient对象
    static MinioClient minioClient =
        MinioClient.builder()
        .endpoint("http://127.0.0.1:9000")
        .credentials("minioadmin", "minioadmin")
        .build();
    //上传文件
    @Test
    public  void upload() {
        //根据扩展名取出mimeType
        ContentInfo extensionMatch = ContentInfoUtil.findExtensionMatch(".mp4");
        // 对于不含文件扩展名或文件扩展名未知的文件,字节流是通用的mimeType
        String mimeType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
        if(extensionMatch!=null){
            mimeType = extensionMatch.getMimeType();
        }
        try {
            UploadObjectArgs testbucket = UploadObjectArgs.builder()
                .bucket("testbucket")
                //.object("test001.mp4") // 在桶的根目录中指定上传的文件名及其扩展名
                .object("001/test001.mp4") // 在桶的根目录下的子目录中指定上传文件的文件名及其扩展名,文件名不能重复
                .filename("D:\develop\upload\1mp4.temp")// 上传的源文件
                //.contentType("video/mp4")// 默认会根据扩展名确定文件内容类型,也可以手动指定
                .build();
            minioClient.uploadObject(testbucket);
            System.out.println("上传成功");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("上传失败");
        }

    }
}

删除文件

@Test
public void delete(){
    try {
        minioClient.removeObject(
            RemoveObjectArgs.builder().bucket("testbucket").object("001/test001.mp4").build());
        System.out.println("删除成功");
    } catch (Exception e) {
        e.printStackTrace();
        System.out.println("删除失败");
    }
}

查询文件

通过查询文件查看文件是否存在minio文件系统中

// IO流的读取简化
@Test
public void getFile() {
    GetObjectArgs getObjectArgs = GetObjectArgs.builder().bucket("testbucket").object("test001.mp4").build();
    try(
        FilterInputStream inputStream = minioClient.getObject(getObjectArgs);
        FileOutputStream outputStream = new FileOutputStream(new File("D:\develop\upload\1_2.mp4"));
     ) {
        IOUtils.copy(inputStream,outputStream);
     } catch (Exception e) {
        e.printStackTrace();
     }
}

查询并下载文件

查看文件是否存在minio文件系统中,并且可以将查询到的字节流写入到本地文件中

@Test
public void getFileTest() {
    try {
        InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
                                                        .bucket("testbucket")
                                                        .object("pic01.png")
                                                        .build());
        FileOutputStream fileOutputStream = new FileOutputStream("C:\Users\15863\Desktop\tmp.png");
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inputStream.read(buffer)) != -1) {
            fileOutputStream.write(buffer,0,len);
        }
        inputStream.close();
        fileOutputStream.close();
        System.out.println("下载成功");
    } catch (Exception e) {
        System.out.println("下载失败");
    }
}
// IO流的读取简化
@Test
public void getFile() {
    GetObjectArgs getObjectArgs = GetObjectArgs.builder().bucket("testbucket").object("test001.mp4").build();
    try(
        FilterInputStream inputStream = minioClient.getObject(getObjectArgs);
        FileOutputStream outputStream = new FileOutputStream(new File("D:\develop\upload\1_2.mp4"));
     ) {
        IOUtils.copy(inputStream,outputStream);
     } catch (Exception e) {
        e.printStackTrace();
     }
}