当前位置:   article > 正文

搭建自己的云存储空间|FastDFS分布式文件系统考虑一下?_morunchang/fastdfs

morunchang/fastdfs

. 前言

最近有很多小伙伴问壹哥,大规模的文件存储该怎么做? 这个感觉很难实现呢。其实这个需求,并没有大家想的那么难以实现。今天壹哥就带着各位,用10分钟的时间来搞定这个需求。不信?那你就跟着壹哥一步步来,10分钟绝对带你学会。

. 浅析FastDFS分布式文件系统

FastDFS是一个开源的轻量级分布式文件系统,它特别适合海量的文件存储,具有文件上传、下载、文件同步和删除等功能。还可以存储各种类型文件,例如文本文件、声音文件、视频文件、图片文件等,可以用来做相册网站,还可以在电商网站中保存商品图片,在贴吧网站保存文章中的插图等。

FastDFS特点:

  • 支持高并发存储,对高并发文件存储有负载均衡的功能;

  • 同时支持存储容量的水平扩展,可以无限扩展增大存储容量;

  • 支持容灾功能,因为内部有冗余备份功能,防止文件丢失;

  • 高可用,管理端和存储端都有心跳检测功能。

不要惊讶它就是这么强大!!!

. FastDFS执行流程和原理

 接下来壹哥就给大家解读FastDFS流程图中展示的执行流程和原理

  • FastDFS分为客户端client(我们的项目),tracker管理端(管理存储端服务器的,不存储文件),storage存储端(存储文件) 共三部分。

  • 首先管理端tracker不存储具体的文件,它用来管理storage存储端集群服务器。tracker可以是一主多从,备机每隔一段时间ping主机,主机返回pong命令,具有心跳检测机制。如果主机宕机,不返回pong命令,那么备机会替代主机工作。请求进入靠tracker管理端分配,具体将文件存储到哪一个storage存储端服务器,具有负载均衡功能。

  • storage存储端服务器,是两台为一组,一台主机和一台备机。存储时向主机从存储,然后主机将文件同步到备机保存一份,冗余存储防止文件丢失。主机和备机之间也有心跳检测机制,防止主机宕机,高可用。存储端服务器可以水平无限扩展,这样理论上fastDFS分布式文件系统可以存储的文件大小是无限大。

  • 客户端如果想要存储,发送请求到tracker管理端,管理端会返回给客户端一个存储端服务器的ip和端口,然后客户端向具体的存储端服务器中存储,存储后返回一个存储后的路径,并且文件会被自动重命名,防止文件重名。

. FastDFS安装部署

壹哥在这里使用Docker容器化来搭建FastDFS。

4.1 拉取镜像

docker pull morunchang/fastdfs

4.2 运行tracker

创建FastDFS管理端tracker容器。

docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh

4.3 运行storage

创建FastDFS存储端storage容器。

  1. # 命令语法
  2. docker run -d --name storage --net=host -e TRACKER_IP=<your tracker server address>:22122 -e GROUP_NAME=<group name> morunchang/fastdfs sh storage.sh
  3. # 创建storage容器例子:
  4. docker run -d --name storage --net=host -e TRACKER_IP=192.168.200.128:22122 -e GROUP_NAME=group1 morunchang/fastdfs sh storage.sh
  • 使用的网络模式是–net=host;

  • 其中<your tracker server address> 位置替换为你机器的ip地址即可;

  • 其中<group name> 是组名,即storage的组,例如: group1、group2、group3等;

  • 如果想要增加新的storage服务器,再次运行该命令,注意更换新组名即可。

4.4 修改nginx的配置

进入storage的容器内部,修改nginx.conf。

# 进入到storage容器内部 docker exec -it storage /bin/bash

进入到容器内部后。

  1. #1. 通过命令来查询Nginx的安装位置:
  2. root@iZ8vb6w2xyjemtqcxtmaj4Z:/# whereis nginx
  3. #显示如下:
  4. nginx: /etc/nginx
  5. #2. 查看当前Nginx的进程
  6. root@iZ8vb6w2xyjemtqcxtmaj4Z:/# ps aux | grep nginx
  7. #显示如下:
  8. root 16 0.0 0.0 32480 1480 ? Ss 13:18 0:00 nginx: master process /etc/nginx/sbin/nginx
  9. nobody 100 0.0 0.0 33036 2116 ? S 14:15 0:00 nginx: worker process
  10. root 118 0.0 0.0 11272 728 pts/1 S+ 14:54 0:00 grep --color=auto nginx

 在storage存储端容器的nginx中添加以下内容:

  1. #3. 修改Nginx的配置文件
  2. vi /etc/nginx/conf/nginx.conf
  3. #4. 修改Nginx配置内容
  4. server {
  5. listen 80;
  6. server_name localhost;
  7. location ~ /M00 {
  8. # storage 实际存储图片的位置
  9. root /data/fast_data/data;
  10. ngx_fastdfs_module;
  11. }
  12. }
  13. #5. 进入到Nginx sbin目录从新加载Nginx配置文件
  14. cd /etc/nginx/sbin
  15. #6. 重新加载配置文件, 让nginx配置生效
  16. ./nginx -s reload

修改后:

storage存储的位置/data/fast_data/data。

 4.5 设置开机启动容器

docker update --restart=always tracker docker update --restart=always storage

. 代码实现

5.1 创建文件管理微服务

创建文件管理微服务fastdfsDemo,该工程主要用于实现文件上传以及文件删除等功能。创建微服务时,项目为Maven项目,不要选择骨架。

5.2 修改pom.xml引入依赖

  1. <!-- 继承Spring boot工程 -->
  2. <parent>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-parent</artifactId>
  5. <version>2.1.5.RELEASE</version>
  6. </parent>
  7. <properties>
  8. <!-- 项目源码及编译输出的编码 -->
  9. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  10. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  11. <!-- 项目编译JDK版本 -->
  12. <maven.compiler.source>1.8</maven.compiler.source>
  13. <maven.compiler.target>1.8</maven.compiler.target>
  14. <!-- 依赖包版本管理 -->
  15. <spring.boot.version>2.1.5.RELEASE</spring.boot.version>
  16. <fastdfs.client.version>1.27.0.0</fastdfs.client.version>
  17. </properties>
  18. <dependencies>
  19. <dependency>
  20. <groupId>org.springframework.boot</groupId>
  21. <artifactId>spring-boot-starter-web</artifactId>
  22. <version>${spring.boot.version}</version>
  23. </dependency>
  24. <dependency>
  25. <groupId>net.oschina.zcx7878</groupId>
  26. <artifactId>fastdfs-client-java</artifactId>
  27. <version>${fastdfs.client.version}</version>
  28. </dependency>
  29. </dependencies>

5.3 创建fasfDFS的配置文件

在resources文件夹下创建fasfDFS的配置文件fdfs_client.conf。

  1. connect_timeout = 60
  2. network_timeout = 60
  3. charset = UTF-8
  4. http.tracker_http_port = 80
  5. tracker_server = 192.168.200.128:22122
  • connect_timeout:连接超时时间,单位为秒;

  • network_timeout:通信超时时间,单位为秒。发送或接收数据时。假设在超时时间后还不能发送或接收数据,则本次网络通信失败;

  • charset: 字符集;

  • http.tracker_http_port :.tracker的http端口;

  • tracker_server:tracker服务器IP和端口设置。

5.4 创建微服务配置文件

在resources文件夹下创建application.yml。

  1. spring:
  2. servlet:
  3. multipart:
  4. max-file-size: 10MB
  5. max-request-size: 10MB
  6. application:
  7. name: fastdfs-demo
  8. server:
  9. port: 9001

max-file-size是单个文件大小,max-request-size是设置总上传的数据大小。

创建启动类

创建com.qianfeng包,创建启动类FastDFSApplication。

  1. package com.qianfeng;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. /**
  5. * 项目启动类
  6. * @Author 千锋壹哥
  7. */
  8. @SpringBootApplication
  9. public class FastDFSApplication {
  10. public static void main(String[] args) {
  11. SpringApplication.run(FastDFSApplication.class, args);
  12. }
  13. }

5.5 文件上传

5.5.1 文件信息封装

文件上传一般都有文件的名字、文件的内容、文件的扩展名、文件的md5值、文件的作者等相关属性,我们可以创建一个对象封装这些属性。我们先创建com.qianfeng.pojo.FastDFSFile文件,代码如下:

  1. package com.qianfeng.pojo;
  2. /**
  3. * 自定义封装, 文件实体类
  4. * @Author 千锋壹哥
  5. */
  6. public class FastDFSFile {
  7. //文件名字
  8. private String name;
  9. //文件内容
  10. private byte[] content;
  11. //文件扩展名
  12. private String ext;
  13. //文件MD5摘要值
  14. private String md5;
  15. //文件创建作者
  16. private String author;
  17. public FastDFSFile(String name, byte[] content, String ext, String height, String width, String author) {
  18. super();
  19. this.name = name;
  20. this.content = content;
  21. this.ext = ext;
  22. this.author = author;
  23. }
  24. public FastDFSFile(String name, byte[] content, String ext) {
  25. super();
  26. this.name = name;
  27. this.content = content;
  28. this.ext = ext;
  29. }
  30. // getter and setter ...
  31. }

5.5.2 文件操作

创建FastDFSClient类,存放在com.qianfeng.util下,在该类中实现FastDFS信息获取以及文件的相关操作,代码如下:

  1. package com.qianfeng.util;
  2. import com.qianfeng.pojo.FastDFSFile;
  3. import org.csource.common.NameValuePair;
  4. import org.csource.fastdfs.*;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.core.io.ClassPathResource;
  7. import java.io.ByteArrayInputStream;
  8. import java.io.IOException;
  9. import java.io.InputStream;
  10. /**
  11. * 上传下载等文件操作工具类
  12. * @Author 千锋壹哥
  13. */
  14. public class FastDFSClient {
  15. private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class);
  16. /***
  17. * 初始化加载FastDFS的TrackerServer配置
  18. */
  19. static {
  20. try {
  21. String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
  22. ClientGlobal.init(filePath);
  23. } catch (Exception e) {
  24. logger.error("FastDFS Client Init Fail!",e);
  25. }
  26. }
  27. /***
  28. * 文件上传
  29. * @param file
  30. * @return
  31. */
  32. public static String[] upload(FastDFSFile file) {
  33. //获取文件的作者
  34. NameValuePair[] meta_list = new NameValuePair[1];
  35. meta_list[0] = new NameValuePair("author", file.getAuthor());
  36. //接收返回数据
  37. String[] uploadResults = null;
  38. StorageClient storageClient=null;
  39. try {
  40. //创建StorageClient客户端对象
  41. storageClient = getTrackerClient();
  42. /***
  43. * 文件上传
  44. * 1)文件字节数组
  45. * 2)文件扩展名
  46. * 3)文件作者
  47. */
  48. uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
  49. } catch (Exception e) {
  50. logger.error("Exception when uploadind the file:" + file.getName(), e);
  51. }
  52. if (uploadResults == null && storageClient!=null) {
  53. logger.error("upload file fail, error code:" + storageClient.getErrorCode());
  54. }
  55. //获取组名
  56. String groupName = uploadResults[0];
  57. //获取文件存储路径
  58. String remoteFileName = uploadResults[1];
  59. return uploadResults;
  60. }
  61. /***
  62. * 获取文件信息
  63. * @param groupName:组名
  64. * @param remoteFileName:文件存储完整名
  65. * @return
  66. */
  67. public static FileInfo getFile(String groupName, String remoteFileName) {
  68. try {
  69. StorageClient storageClient = getTrackerClient();
  70. return storageClient.get_file_info(groupName, remoteFileName);
  71. } catch (Exception e) {
  72. logger.error("Exception: Get File from Fast DFS failed", e);
  73. }
  74. return null;
  75. }
  76. /***
  77. * 文件下载
  78. * @param groupName
  79. * @param remoteFileName
  80. * @return
  81. */
  82. public static InputStream downFile(String groupName, String remoteFileName) {
  83. try {
  84. //创建StorageClient
  85. StorageClient storageClient = getTrackerClient();
  86. //下载文件
  87. byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
  88. InputStream ins = new ByteArrayInputStream(fileByte);
  89. return ins;
  90. } catch (Exception e) {
  91. logger.error("Exception: Get File from Fast DFS failed", e);
  92. }
  93. return null;
  94. }
  95. /***
  96. * 文件删除
  97. * @param groupName
  98. * @param remoteFileName
  99. * @throws Exception
  100. */
  101. public static void deleteFile(String groupName, String remoteFileName)
  102. throws Exception {
  103. //创建StorageClient
  104. StorageClient storageClient = getTrackerClient();
  105. //删除文件
  106. int i = storageClient.delete_file(groupName, remoteFileName);
  107. }
  108. /***
  109. * 获取Storage组
  110. * @param groupName
  111. * @return
  112. * @throws IOException
  113. */
  114. public static StorageServer[] getStoreStorages(String groupName)
  115. throws IOException {
  116. //创建TrackerClient
  117. TrackerClient trackerClient = new TrackerClient();
  118. //获取TrackerServer
  119. TrackerServer trackerServer = trackerClient.getConnection();
  120. //获取Storage组
  121. return trackerClient.getStoreStorages(trackerServer, groupName);
  122. }
  123. /***
  124. * 获取Storage信息,IP和端口
  125. * @param groupName
  126. * @param remoteFileName
  127. * @return
  128. * @throws IOException
  129. */
  130. public static ServerInfo[] getFetchStorages(String groupName,
  131. String remoteFileName) throws IOException {
  132. TrackerClient trackerClient = new TrackerClient();
  133. TrackerServer trackerServer = trackerClient.getConnection();
  134. return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName);
  135. }
  136. /***
  137. * 获取Tracker服务地址
  138. * @return
  139. * @throws IOException
  140. */
  141. public static String getTrackerUrl() throws IOException {
  142. return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":"+ ClientGlobal.getG_tracker_http_port()+"/";
  143. }
  144. /***
  145. * 获取Storage客户端
  146. * @return
  147. * @throws IOException
  148. */
  149. private static StorageClient getTrackerClient() throws IOException {
  150. TrackerServer trackerServer = getTrackerServer();
  151. StorageClient storageClient = new StorageClient(trackerServer, null);
  152. return storageClient;
  153. }
  154. /***
  155. * 获取Tracker
  156. * @return
  157. * @throws IOException
  158. */
  159. private static TrackerServer getTrackerServer() throws IOException {
  160. TrackerClient trackerClient = new TrackerClient();
  161. TrackerServer trackerServer = trackerClient.getConnection();
  162. return trackerServer;
  163. }
  164. }

5.5.3 文件上传

创建一个FileController,在该控制器中实现文件上传操作,代码如下:

  1. package com.qianfeng.controller;
  2. import com.qianfeng.pojo.FastDFSFile;
  3. import com.qianfeng.util.FastDFSClient;
  4. import org.springframework.web.bind.annotation.*;
  5. import org.springframework.web.multipart.MultipartFile;
  6. import java.io.IOException;
  7. /**
  8. * 文件操作controller接口
  9. * @Author 千锋壹哥
  10. */
  11. @RestController
  12. @CrossOrigin
  13. @RequestMapping("/file")
  14. public class FileController {
  15. /**
  16. * 上传接口
  17. * @param file 接收文件参数, 参数名必须叫做file
  18. * @Author 千锋壹哥
  19. */
  20. @PostMapping("/upload")
  21. public String upload(@RequestParam("file") MultipartFile file) {
  22. String path ="";
  23. try {
  24. path=saveFile(file);
  25. System.out.println(path);
  26. } catch (Exception e) {
  27. e.printStackTrace();
  28. }
  29. return path;
  30. }
  31. /**
  32. * 上传文件到FastDFS分布式文件系统
  33. * @param multipartFile
  34. * @Author 千锋壹哥
  35. */
  36. public String saveFile(MultipartFile multipartFile) throws IOException {
  37. //1. 获取文件名
  38. String fileName = multipartFile.getOriginalFilename();
  39. //2. 获取文件内容
  40. byte[] content = multipartFile.getBytes();
  41. //3. 获取文件扩展名
  42. String ext = "";
  43. if (fileName != null && !"".equals(fileName)) {
  44. ext = fileName.substring(fileName.lastIndexOf("."));
  45. }
  46. //4. 创建文件实体类对象
  47. FastDFSFile fastDFSFile = new FastDFSFile(fileName, content, ext);
  48. //5. 上传
  49. String[] uploadResults = FastDFSClient.upload(fastDFSFile);
  50. //6. 拼接上传后的文件的完整路径和名字, uploadResults[0]为组名, uploadResults[1]为文件名称和路径
  51. String path = FastDFSClient.getTrackerUrl() + uploadResults[0] + "/" + uploadResults[1];
  52. //7. 返回
  53. return path;
  54. }
  55. }

5.4 Postman测试文件上传

测试步骤:

  1. 选择post请求方式,输入请求地址 http://localhost:9001/file/upload

  1. 填写Headers

Key:Content-Type Value:multipart/form-data

  1. 填写body。选择form-data 然后选择文件file 点击添加文件,最后发送即可。

postman填写信息填写如下步骤所示。

注意Headers请求头中内容:

 注意body请求体中内容:

上传结果后,就可以通过浏览器访问显示如下效果,这个小姐姐你想要吗? 

六. 结语

至此,各位小伙伴是否都已经跟着壹哥学会了FastDFS的使用了呢?

现在挺多企业都是购买阿里云或者华为云等云服务器,然后在这些云服务器上再手动部署FastDFS,这样比直接购买使用阿里的OSS对象存储服务更节省经费,这也就是FastDFS的魅力所在。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/170118
推荐阅读
相关标签
  

闽ICP备14008679号