分布式文件系统(FastDFS)

简介:java系列技术分享(持续更新中…)
初衷:一起学习、一起进步、坚持不懈
如果文章内容有误与您的想法不一致,欢迎大家在评论区指正
希望这篇文章对你有所帮助,欢迎点赞 收藏 ⭐留言

更多文章请点击
在这里插入图片描述在这里插入图片描述

文章目录

  • 一、 FastDFS简介
  • 二、 FastDFS内部结构
  • 三、 FastDFS存储策略
  • 四、 工作原理
    • 4.1 上传流程
    • 4.2 下载流程
  • 五、 收成文件名解读
  • 六、 使用步骤
    • 6.1 引入依赖
    • 6.2 配置文件application.yml
    • 6.3 测试
    • 6.4 工具类

分布式文件系统(FastDFS)_第1张图片

一、 FastDFS简介

项目开源地址 :https://github.com/happyfish100/fastdfs
官方论坛: http://bbs.chinaunix.net/forum-240-1.html

  • FastDFS是基于互联网应用的开源分布式文件系统,主要用于大中型网站存储资源文件,如图片、文档、音频、视频等。
  • FastDFS 是基于 C 语言开发的,是一个轻量级开源的高性能分布式文件系统。主要功能有:文件存储、文件同步、文件访问(文件上传/下载),解决了大容量的文件存储和高并发访问的问题,文件存取时实现了负载均衡。

二、 FastDFS内部结构

FastDFS 架构包括 Tracker ServerStorage Server

  • Storage Server : 存储服务器,负责文件存储,文件同步/备份,提供文件访问接口,文件元数据管理。以 group 为单位,每个 group 内可以有多台 Storage Server,数据互为备份,达到容灾的目的。每个 Storage 在启动以后会主动连接 Tracker,告知自己所属 group 等存储相关信息,并保持周期性心跳。

  • Tracker Server:  跟踪调度服务器,调度存储服务,负责文件访问的调度和负载均衡,负责管理所有的 Storage Server 和 group 组/卷。

  • Group: 组, 也可称为 Volume 卷。同组内服务器上的文件是完全相同的,同一组内的 Storage Server 之间是对等的,文件上传、删除等操作可以在任意一台 Storage Server 上进行。

三、 FastDFS存储策略

  • 扩容分组可以解决单一Storage Server 容量限制问题
  • 组内构建存储服务器,解决单一节点故障问题
  1. 为了支持大容量存储,Storage 存储服务器采用了分组(或分卷)的方式。存储系统由一个或多个组组成,组与组之间的文件是相互独立的,所有组的文件容量累加就是整个存储系统中的文件容量。一个组可以由一台或多台存储服务器组成,一个组下的存储服务器中的文件都是相同的,组中的多台存储服务器起到了冗余备份和负载均衡的作用。

  2. 当组中增加了新的服务器时,系统会自动同步已有的文件,同步完成后,系统自动将新增的服务器切换至线上提供服务。

  3. 当存储空间不足时,可以动态添加组,只需要增加一台或多台服务器,并将它们配置为一个新的组,即可扩大存储系统的容量。当你的某个应用或者模块(对应的 group)的并发过高的时候,可以直接在 group 中增加若干个 Storage 来实现负载均衡。

四、 工作原理

4.1 上传流程

分布式文件系统(FastDFS)_第2张图片

4.2 下载流程

分布式文件系统(FastDFS)_第3张图片

五、 收成文件名解读

生成文件名 : 文件名由 group名称/存储目录/两级子目录/file_id.后缀名 拼接而成。

例如: group/M00/00/00/wkiIoGjXKDSAE030aaCXPfGRATQ725.jpg

解读:

  • group1组名/卷名。文件上传成功以后所在的 Storage 组名称,由 Storage 服务器返回。
  • M00虚拟磁盘路径
  • /00/00数据两级目录 Storage 服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。
  • wkiIoGjXKDSAE030aaCXPfGRATQ725file_id,由 Storage Server IP、文件创建时间、文件大小、文件 crc32 和一个随机数组成,然后将这个二进制串进行 base64 编码,转换为字符串。

六、 使用步骤

6.1 引入依赖

<dependency>
    <groupId>com.github.tobatogroupId>
    <artifactId>fastdfs-clientartifactId>
    <version>1.26.7version>
    <exclusions>
        <exclusion>
            <groupId>ch.qos.logbackgroupId>
            <artifactId>logback-classicartifactId>
        exclusion>
    exclusions>
dependency>

6.2 配置文件application.yml


fdfs:
  so-timeout: 1500
  connect-timeout: 600
  #缩略图生成参数
  thumb-image:
    width: 150
    height: 150
  #TrackerList参数,支持多个
  tracker-list: 192.168.136.150:22122
  ## 可选,主要用于路径拼接
  web-server-url: http://192.168.136.150:8888/ 

6.3 测试

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TanhuaServerApplication.class)
public class TestFastDFS {

    //从调度服务器获取一个目标存储服务器,上传
    @Autowired
    private FastFileStorageClient client;

    @Autowired
    private FdfsWebServer webServer;// 获取存储服务器的请求URL 对应配置文件中的 web-server-url

    @Test
    public void testFileUpdate() throws FileNotFoundException {
         //1、指定文件
        File file = new File("D:\\test.jpg");
        //2、文件上传
        StorePath path = client.uploadFile(new FileInputStream(file),
                file.length(), "jpg", null);
        //3、拼接访问路径
        String url = webServer.getWebServerUrl() + path.getFullPath();
    }
}


6.4 工具类

  1. 接口
public interface FdfsService {

    String uploadFile(MultipartFile file);

    String downloadFile(String fileUrl);

    Boolean deleteFile(String fileUrl);

    byte[] downloadMultipartFile(String fileUrl);
}
  1. 实现类
@Service
@Slf4j
public class FdfsServiceImpl implements FdfsService {

    @Autowired
    private FastDFSClientWrapper fastDFSClientWrapper;

    /**
     * 文件上传
     * 最后返回fastDFS中的文件名称;group/M00/00/00/wkiIoGjXKDSAE030aaCXPfGRATQ725.jpg
     */
    @Override
    public String uploadFile(MultipartFile file) {
        byte[] bytes = new byte[0];
        try {
            bytes = file.getBytes();
        } catch (IOException e) {
            log.error("获取文件错误");
            e.printStackTrace();
        }
        //获取源文件名称
        String originalFileName = file.getOriginalFilename();
        //获取文件后缀
        String extension = originalFileName.substring(originalFileName.lastIndexOf(".") + 1);
        String fileName = file.getName();
        //获取文件大小
        long fileSize = file.getSize();
        return fastDFSClientWrapper.uploadFile(bytes, fileSize, extension);
    }

    /**
     * 文件下载
     *
     * @param fileUrl  当前对象文件名称
     * @param response HttpServletResponse 内置对象
     * @throws IOException
     */
    public void downloadFile(String fileUrl, HttpServletResponse response) throws IOException {
        byte[] bytes = fastDFSClientWrapper.downloadFile(fileUrl);
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("test.jpg", "UTF-8"));
        response.setCharacterEncoding("UTF-8");
        ServletOutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            outputStream.write(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.flush();
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public Boolean deleteFile(String fileUrl) {
        try {
            fastDFSClientWrapper.deleteFile(fileUrl);
        } catch (IOException e) {
            log.error("文件删除失败",e);
            return false;
        }
        return true;
    }

    @Override
    public byte[] downloadMultipartFile(String fileUrl) {
        byte[] bytes = new byte[0];
        MultipartFile file = null;
        try {
            bytes = fastDFSClientWrapper.downloadFile(fileUrl);
        } catch (IOException e) {
            log.error("文件下载失败",e);
        }
        return bytes;
    }

    /**
     * 文件下载
     * @param fileUrl  当前对象文件名称
     * @throws IOException
     */
    @Override
    public String downloadFile(String fileUrl)  {
        byte[] bytes = new byte[0];
        try {
            bytes = fastDFSClientWrapper.downloadFile(fileUrl);
        } catch (IOException e) {
            log.error("文件下载失败",e);
        }
        return Base64.getEncoder().encodeToString(bytes);
    }


}

  1. 客户端包装类
@Component
@Slf4j
public class FastDFSClientWrapper {

    @Autowired
    private FastFileStorageClient fastFileStorageClient;

    /**
     * 文件上传
     * 最后返回fastDFS中的文件名称;group/M00/00/00/wkiIoGjXKDSAE030aaCXPfGRATQ725.jpg
     *
     * @param bytes     文件字节
     * @param fileSize  文件大小
     * @param extension 文件扩展名
     * @return fastDfs路径
     */
    public String uploadFile(byte[] bytes, long fileSize, String extension) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        StorePath storePath = fastFileStorageClient.uploadFile(byteArrayInputStream, fileSize, extension, null);
        System.out.println(storePath.getGroup() + "==" + storePath.getPath() + "======" + storePath.getFullPath());
        return storePath.getFullPath();
    }

    /**
     * 下载文件
     *  返回文件字节流大小
     * @param fileUrl 文件URL
     * @return 文件字节
     */
    public byte[] downloadFile(String fileUrl) throws IOException {
        String group = fileUrl.substring(0, fileUrl.indexOf("/"));
        String path = fileUrl.substring(fileUrl.indexOf("/") + 1);
        DownloadByteArray downloadByteArray = new DownloadByteArray();
        byte[] bytes = fastFileStorageClient.downloadFile(group, path, downloadByteArray);
        return bytes;
    }

    public boolean deleteFile(String fileUrl) throws IOException {
        String group = fileUrl.substring(0, fileUrl.indexOf("/"));
        String path = fileUrl.substring(fileUrl.indexOf("/") + 1);
        fastFileStorageClient.deleteFile(group,path);
        return true;
    }

}

在这里插入图片描述在这里插入图片描述

你可能感兴趣的:(分布式文件存储,java,spring,boot)