项目中使用,又把原来忘记的东西重新捡起来,整理一份

FastDFS基本介绍

FastDFS 系统三个角色

Tracker Server

Tracker Server: 跟踪服务器,主要做调度工作,起到均衡的作用;负责管理所有的 storage server
和 group,每个 storage 在启动后会连接 Tracker,告知自己所属 group 等信息,并保持周期性心跳。

Storage Server


Storage Server:存储服务器,主要提供容量和备份服务;以 group 为单位,每个 group 内可以有多台 storage server,数据互为备份。

Client

Client:客户端,上传下载数据的服务器,也就是我们自己的项目所部署在的服务器。
FastDFS文件系统单机环境搭建 - 图1

fastDFS优点:1.高可靠性:无单点故障 2.高吞吐性:只要 Group 足够多,数据流量将足够分散。

搭建fastDFS(阿里服务器)

安装 libfastcommon 和 FastDFS

一、安装 libfastcommon


这里是通过wget下载:

  1. wget https://github.com/happyfish100/libfastcommon/archive/V1.0.7.tar.gz

解压 libfastcommon,命令:

  1. tar -zxvf V1.0.7.tar.gz

移动解压目录到/usr/local下:

  1. mv libfastcommon-1.0.7 /usr/local/

进入libfastcommon-1.0.7目录,命令:

  1. cd /usr/local/libfastcommon-1.0.7

编译

  1. ./make.sh

安装:

  1. ./make.sh install

安装 libfastcommon 成功:
FastDFS文件系统单机环境搭建 - 图2

配置

配置 Tracker 服务


上述安装成功后,在/etc/目录下会有一个fdfs的目录,进入它。会看到三个.sample后缀的文件,这是作者给我们的示例文件,我们需要把其中的tracker.conf.sample文件改为tracker.conf配置文件并修改它。参照以下命令:

  1. cd /etc/fdfs/
  2. cp tracker.conf.sample tracker.conf
  3. vim tracker.conf
  1. // 编辑tracker.conf文件,找到你需要修改的两个参数就可以了,我这里不做修改,用默认的
  2. # the base path to store data and log files // 数据和日志的存放目录
  3. base_path=/home/yuqing/fastdfs
  4. # HTTP port on this tracker server // http服务端口
  5. http.server_port=8070

修改完成后我们需要建立tracker的工作目录,不然启动报错(上面base_path配置的目录)

  1. mkdir -p /home/yuqing/fastdfs

启动tracker服务:

  1. /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf start

查看监听:

  1. ps -ef|grep fdfs
  2. #或
  3. netstat -lnpt|grep fdfs

Tracker服务安装成功,启动成功:
FastDFS文件系统单机环境搭建 - 图3

pat(disk or mount point) count, default value is 1 // storage在存储文件时支持多路径,默认只设置一个(下面store_path0配置的数量个数)

store_path_count=1

store_path#, based 0, if store_path0 not exists, it’s value is base_path // 配置多个store_path路径,从0开始,如果store_path0不存在,则base_path必须存在 the paths must be exist // 此处可以配置多个路径,如:store_path0=xx, store_path1=xx,store_path2=xx
// 多个项目的文件按照文件夹分割,通过/usr/bin/fdfs_upload_file [storage_ip:port] [store_path_index] 选择storage服务器和store_path角标上传到相应的项目下
store_path0=/home/yuqing/fastdfs tracker_server can ocur more than once, and tracker_server format is // 设置tracker_server换成自己的IP端口 “host:port”, host can be hostname or ip address tracker_server=192.168.2.121:22122 修改完成后我们需要建立tracker的工作目录,不然启动报错(上面base_path配置的目录和store_path0配置的目录)

mkdir -p /home/yuqing/fastdfs

启动storage服务:

  1. /usr/bin/fdfs_storaged /etc/fdfs/storage.conf start

查看监听:

  1. ps -ef|grep fdfs netstat -lnpt|grep fdfs

22122 和 23000端口都在监听:(/home/yuqing/fastdfs文件夹下看的话,会出现一大堆文件夹)
FastDFS文件系统单机环境搭建 - 图4

到这里,我们安装配置并启动了 Tracker 和 Storage 服务,也没有报错了。那他俩是不是在通信呢?我们可以监视一下:

  1. /usr/bin/fdfs_monitor /etc/fdfs/storage.conf

红线处ACTIVE成功:
FastDFS文件系统单机环境搭建 - 图5

配置 fdfs_upload_file 上传文件

依然是进入/etc/fdfs的目录操作,首先进入它。会看到三个.sample后缀的文件,我们需要把其中的client.conf.sample文件改为client.conf配置文件并修改它。参照以下命令:

  1. cd /etc/fdfs/
  2. cp client.conf.sample client.conf
  3. vim client.conf

进行修改:

  1. # the base path to store log files // 日志的存放目录
  2. base_path=/home/yuqing/fastdfs
  3. # tracker_server can ocur more than once, and tracker_server format is // tracker_server服务的地址和端口号
  4. # "host:port", host can be hostname or ip address
  5. tracker_server=192.168.2.121:22122

修改完成后我们就可以通过/usr/bin/fdfs_upload_file命令上传文件了,/usr/bin/fdfs_upload_file 命令用法:

  1. [root@localhost fdfs]# /usr/bin/fdfs_upload_file
  2. Usage: /usr/bin/fdfs_upload_file <config_file> <local_filename> [storage_ip:port] [store_path_index]
  3. // 命令 配置文件 上传的文件 storage_ip和端口 store_path角标

上传成功:
FastDFS文件系统单机环境搭建 - 图6

  1. // 配置tracker.conf这个配置文件是否生效,因为在启动fastdfs服务端进程时需要指定配置文件,所以需要使次配置文件生效。false是生效,true是屏蔽。
  2. disabled=false
  3. // 程序的监听地址,如果不设定则监听所有地址
  4. bind_addr=
  5. // tracker监听的端口
  6. port=22122
  7. // 链接超时设定
  8. connect_timeout=30
  9. // tracker在通过网络发送接收数据的超时时间
  10. network_timeout=60
  11. // 数据和日志的存放地点
  12. base_path=/opt/fdfs
  13. // 服务所支持的最大链接数
  14. max_connections=256
  15. // 工作线程数一般为cpu个数
  16. work_threads=4
  17. // 在存储文件时选择group的策略,0:轮训策略 1:指定某一个组 2:负载均衡,选择空闲空间最大的group
  18. store_lookup=2
  19. // 如果上面的store_lookup选择了1,则这里需要指定一个group
  20. // store_group=group2
  21. // 在group中的哪台storage做主storage,当一个文件上传到主storage后,就由这台机器同步文件到group内的其他storage上,0:轮训策略 1:根据ip地址排序,第一个 2:根据优先级排序,第一个
  22. store_server=0
  23. // 选择那个storage作为主下载服务器,0:轮训策略 1:主上传storage作为主下载服务器
  24. download_server=0
  25. // 选择文件上传到storage中的哪个(目录/挂载点),storage可以有多个存放文件的base path 0:轮训策略 2:负载均衡,选择空闲空间最大的
  26. store_path=0
  27. // 系统预留空间,当一个group中的任何storage的剩余空间小于定义的值,整个group就不能上传文件了
  28. reserved_storage_space = 4GB
  29. // 日志信息级别
  30. log_level=info
  31. // 进程以那个用户/用户组运行,不指定默认是当前用户
  32. run_by_group=
  33. run_by_user=
  34. // 允许那些机器连接tracker默认是所有机器
  35. allow_hosts=*
  36. // 设置日志信息刷新到disk的频率,默认10s
  37. sync_log_buff_interval = 10
  38. // 检测storage服务器的间隔时间,storage定期主动向tracker发送心跳,如果在指定的时间没收到信号,tracker人为storage故障,默认120s
  39. check_active_interval = 120
  40. // 线程栈的大小,最小64K
  41. thread_stack_size = 64KB
  42. // storage的ip改变后服务端是否自动调整,storage进程重启时才自动调整
  43. storage_ip_changed_auto_adjust = true
  44. // storage之间同步文件的最大延迟,默认1天
  45. storage_sync_file_max_delay = 86400
  46. // 同步一个文件所花费的最大时间
  47. storage_sync_file_max_time = 300
  48. // 是否用一个trunk文件存储多个小文件
  49. use_trunk_file = false
  50. // 最小的solt大小,应该小于4KB,默认256bytes
  51. slot_min_size = 256
  52. // 最大的solt大小,如果上传的文件小于默认值,则上传文件被放入trunk文件中
  53. slot_max_size = 16MB
  54. // trunk文件的默认大小,应该大于4M
  55. trunk_file_size = 64MB
  56. // http服务是否生效,默认不生效
  57. http.disabled=false
  58. // http服务端口
  59. http.server_port=8080
  60. // 检测storage上http服务的时间间隔,<=0表示不检测
  61. http.check_alive_interval=30
  62. // 检测storage上http服务时所用请求的类型,tcp只检测是否可以连接,http必须返回200
  63. http.check_alive_type=tcp
  64. // 通过url检测storage http服务状态
  65. http.check_alive_uri=/status.html
  66. // if need find content type from file extension name
  67. http.need_find_content_type=true
  68. // 用include包含进http的其他设置
  69. // include http.conf
  1. ## storage.conf配置文件
  2. ```java
  3. storage.conf配置文件分析:
  4. // 同tracker.conf
  5. disabled=false
  6. // 这个storage服务器属于那个group
  7. group_name=group1
  8. // 同tracker.conf
  9. bind_addr=
  10. // 连接其他服务器时是否绑定地址,bind_addr配置时本参数才有效
  11. client_bind=true
  12. // 同tracker.conf
  13. port=23000
  14. connect_timeout=30
  15. network_timeout=60
  16. // 主动向tracker发送心跳检测的时间间隔
  17. heart_beat_interval=30
  18. // 主动向tracker发送磁盘使用率的时间间隔
  19. stat_report_interval=60
  20. // 同tracker.conf
  21. base_path=/opt/fdfs
  22. max_connections=256
  23. // 接收/发送数据的buff大小,必须大于8KB
  24. buff_size = 256KB
  25. // 同tracker.conf
  26. work_threads=4
  27. // 磁盘IO是否读写分离
  28. disk_rw_separated = true
  29. // 是否直接读写文件,默认关闭
  30. disk_rw_direct = false
  31. // 混合读写时的读写线程数
  32. disk_reader_threads = 1
  33. disk_writer_threads = 1
  34. // 同步文件时如果binlog没有要同步的文件,则延迟多少毫秒后重新读取,0表示不延迟
  35. sync_wait_msec=50
  36. // 同步完一个文件后间隔多少毫秒同步下一个文件,0表示不休息直接同步
  37. sync_interval=0
  38. // 表示这段时间内同步文件
  39. sync_start_time=00:00
  40. sync_end_time=23:59
  41. // 同步完多少文件后写mark标记
  42. write_mark_file_freq=500
  43. // storage在存储文件时支持多路径,默认只设置一个
  44. store_path_count=1
  45. // 配置多个store_path路径,从0开始,如果store_path0不存在,则base_path必须存在
  46. store_path0=/opt/fdfs
  47. // store_path1=/opt/fastdfs2
  48. // subdir_count * subdir_count个目录会在store_path下创建,采用两级存储
  49. subdir_count_per_path=256
  50. // 设置tracker_server
  51. tracker_server=x.x.x.x:22122
  52. // 同tracker.conf
  53. log_level=info
  54. run_by_group=
  55. run_by_user=
  56. allow_hosts=*
  57. // 文件在数据目录下的存放策略,0:轮训 1:随机
  58. file_distribute_path_mode=0
  59. // 当问及是轮训存放时,一个目录下可存放的文件数目
  60. file_distribute_rotate_count=100
  61. // 写入多少字节后就开始同步,0表示不同步
  62. fsync_after_written_bytes=0
  63. // 刷新日志信息到disk的间隔
  64. sync_log_buff_interval=10
  65. // 同步storage的状态信息到disk的间隔
  66. sync_stat_file_interval=300
  67. // 线程栈大小
  68. thread_stack_size=512KB
  69. // 设置文件上传服务器的优先级,值越小越高
  70. upload_priority=10
  71. // 是否检测文件重复存在,1:检测 0:不检测
  72. check_file_duplicate=0
  73. // 当check_file_duplicate设置为1时,次值必须设置
  74. key_namespace=FastDFS
  75. // 与FastDHT建立连接的方式 0:短连接 1:长连接
  76. keep_alive=0
  77. // 同tracker.conf
  78. http.disabled=false
  79. http.domain_name=
  80. http.server_port=8888
  81. http.trunk_size=256KB
  82. http.need_find_content_type=true
  83. // include http.conf

nginx简单整合fastDFS


这里小编只是简单的整合,只是为了访问方便,
配置nginx的配置文件,nginx.conf文件:

  1. server {
  2. listen 89;#监听89端口
  3. server_name localhost;#ip
  4. location /group1/M00 {## 当访问89端口并且/group1/M00开头的请求,都有映射到fastDFS的存储数据区
  5. alias /usr/local/fastdfs-5.05/files_music/data;# 该地址就是文件存储的数据文件夹
  6. }
  7. }

FastDFS项目中实现文件上传下载


了解FastDFS的工作流程
FastDFS文件系统单机环境搭建 - 图7

一、pom文件中加jar包

  1. <dependency>
  2. <groupId>net.oschina.zcx7878</groupId>
  3. <artifactId>fastdfs-client-java</artifactId>
  4. <version>1.27.0.0</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.apache.commons</groupId>
  8. <artifactId>commons-io</artifactId>
  9. <version>1.3.2</version>
  10. </dependency>

二、配置FastDFS基本信息:

在项目的resource目录下面新建fdfs_client.conf配置文件,配置信息如下:

  1. connect_timeout = 60
  2. network_timeout = 60
  3. charset = UTF-8
  4. http.tracker_http_port = 8070//搭建时的fastDFS的根据配置
  5. tracker_server = 192.168.200.128:22122

二、工具类

封装文件的实体类

  1. public class FastDFSFile {
  2. //文件名字
  3. private String name;
  4. //文件内容
  5. private byte[] content;
  6. //文件扩展名
  7. private String ext;
  8. //文件MD5摘要值
  9. private String md5;
  10. //文件创建作者
  11. private String author;
  12. public FastDFSFile(String name, byte[] content, String ext, String height,
  13. String width, String author) {
  14. super();
  15. this.name = name;
  16. this.content = content;
  17. this.ext = ext;
  18. this.author = author;
  19. }
  20. public FastDFSFile(String name, byte[] content, String ext) {
  21. super();
  22. this.name = name;
  23. this.content = content;
  24. this.ext = ext;
  25. }
  26. public String getName() {
  27. return name;
  28. }
  29. public void setName(String name) {
  30. this.name = name;
  31. }
  32. public byte[] getContent() {
  33. return content;
  34. }
  35. public void setContent(byte[] content) {
  36. this.content = content;
  37. }
  38. public String getExt() {
  39. return ext;
  40. }
  41. public void setExt(String ext) {
  42. this.ext = ext;
  43. }
  44. public String getMd5() {
  45. return md5;
  46. }
  47. public void setMd5(String md5) {
  48. this.md5 = md5;
  49. }
  50. public String getAuthor() {
  51. return author;
  52. }
  53. public void setAuthor(String author) {
  54. this.author = author;
  55. }
  56. }

上传工具类(上传、下载和删除等等操作)

  1. import org.csource.common.NameValuePair;
  2. import org.csource.fastdfs.*;
  3. import org.slf4j.LoggerFactory;
  4. import org.springframework.core.io.ClassPathResource;
  5. import java.io.ByteArrayInputStream;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. /**
  9. * @author Administrator
  10. * @date 2020/12/9 10:58
  11. **/
  12. public class FastDFSClient {
  13. private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class);
  14. /***
  15. * 初始化加载FastDFS的TrackerServer配置
  16. */
  17. static {
  18. try {
  19. String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
  20. ClientGlobal.init(filePath);
  21. } catch (Exception e) {
  22. logger.error("FastDFS Client Init Fail!",e);
  23. }
  24. }
  25. /***
  26. * 文件上传
  27. * @param file
  28. * @return 1.文件的组名 2.文件的路径信息
  29. */
  30. public static String[] upload(FastDFSFile file) {
  31. logger.info("File Name: " + file.getName() + "File Length:" + file.getContent().length);
  32. //获取文件的作者
  33. NameValuePair[] meta_list = new NameValuePair[1];
  34. meta_list[0] = new NameValuePair("author", file.getAuthor());
  35. long startTime = System.currentTimeMillis();
  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. logger.info("upload_file time used:" + (System.currentTimeMillis() - startTime) + " ms");
  56. if (uploadResults == null && storageClient!=null) {
  57. logger.error("upload file fail, error code:" + storageClient.getErrorCode());
  58. }
  59. //获取组名
  60. String groupName = uploadResults[0];
  61. //获取文件存储路径
  62. String remoteFileName = uploadResults[1];
  63. logger.info("upload file successfully!!!" + "group_name:" + groupName + ", remoteFileName:" + " " + remoteFileName);
  64. return uploadResults;
  65. }
  66. /***
  67. * 获取文件信息
  68. * @param groupName:组名
  69. * @param remoteFileName:文件存储完整名
  70. * @return
  71. */
  72. public static FileInfo getFile(String groupName, String remoteFileName) {
  73. try {
  74. StorageClient storageClient = getTrackerClient();
  75. return storageClient.get_file_info(groupName, remoteFileName);
  76. } catch (Exception e) {
  77. logger.error("Exception: Get File from Fast DFS failed", e);
  78. }
  79. return null;
  80. }
  81. /***
  82. * 文件下载
  83. * @param groupName
  84. * @param remoteFileName
  85. * @return
  86. */
  87. public static InputStream downFile(String groupName, String remoteFileName) {
  88. try {
  89. //创建StorageClient
  90. StorageClient storageClient = getTrackerClient();
  91. //下载文件
  92. byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
  93. InputStream ins = new ByteArrayInputStream(fileByte);
  94. return ins;
  95. } catch (Exception e) {
  96. logger.error("Exception: Get File from Fast DFS failed", e);
  97. }
  98. return null;
  99. }
  100. /***
  101. * 文件删除
  102. * @param groupName
  103. * @param remoteFileName
  104. * @throws Exception
  105. */
  106. public static void deleteFile(String groupName, String remoteFileName)
  107. throws Exception {
  108. //创建StorageClient
  109. StorageClient storageClient = getTrackerClient();
  110. //删除文件
  111. int i = storageClient.delete_file(groupName, remoteFileName);
  112. logger.info("delete file successfully!!!" + i);
  113. }
  114. /***
  115. * 获取Storage组
  116. * @param groupName
  117. * @return
  118. * @throws IOException
  119. */
  120. public static StorageServer[] getStoreStorages(String groupName)
  121. throws IOException {
  122. //创建TrackerClient
  123. TrackerClient trackerClient = new TrackerClient();
  124. //获取TrackerServer
  125. TrackerServer trackerServer = trackerClient.getConnection();
  126. //获取Storage组
  127. return trackerClient.getStoreStorages(trackerServer, groupName);
  128. }
  129. /***
  130. * 获取Storage信息,IP和端口
  131. * @param groupName
  132. * @param remoteFileName
  133. * @return
  134. * @throws IOException
  135. */
  136. public static ServerInfo[] getFetchStorages(String groupName,
  137. String remoteFileName) throws IOException {
  138. TrackerClient trackerClient = new TrackerClient();
  139. TrackerServer trackerServer = trackerClient.getConnection();
  140. return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName);
  141. }
  142. /***
  143. * 获取Tracker服务地址
  144. * @return
  145. * @throws IOException
  146. */
  147. public static String getTrackerUrl() throws IOException {
  148. // return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+"/";
  149. return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":89"+"/";//89为nginx监听的端口,有nginx代理监听映射到文件,进行访问
  150. }
  151. /***
  152. * 获取Storage客户端
  153. * @return
  154. * @throws IOException
  155. */
  156. private static StorageClient getTrackerClient() throws IOException {
  157. TrackerServer trackerServer = getTrackerServer();
  158. StorageClient storageClient = new StorageClient(trackerServer, null);
  159. return storageClient;
  160. }
  161. /***
  162. * 获取Tracker
  163. * @return
  164. * @throws IOException
  165. */
  166. private static TrackerServer getTrackerServer() throws IOException {
  167. TrackerClient trackerClient = new TrackerClient();
  168. TrackerServer trackerServer = trackerClient.getConnection();
  169. return trackerServer;
  170. }
  171. }

测试前端页面,提交表上

  1. <form action="/upload" method="post" enctype="multipart/form-data">
  2. <input type="file" name="file">
  3. <input type="submit" value="上传">
  4. </form>

测试后端控制台,上传文件

  1. @Controller
  2. public class UploadController {
  3. private static Logger logger = LoggerFactory.getLogger(UploadController.class);
  4. @PostMapping("/upload")
  5. public String singleFileUpload(@RequestParam("file") MultipartFile file,
  6. RedirectAttributes redirectAttributes) {
  7. if (file.isEmpty()) {
  8. redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
  9. return "redirect:uploadStatus";
  10. }
  11. try {
  12. // Get the file and save it somewhere
  13. String path=saveFile(file);
  14. redirectAttributes.addFlashAttribute("message",
  15. "You successfully uploaded '" + file.getOriginalFilename() + "'");
  16. redirectAttributes.addFlashAttribute("path",
  17. "file path url '" + path + "'");
  18. } catch (Exception e) {
  19. logger.error("upload file failed",e);
  20. }
  21. return "上传成功";
  22. }
  23. /*
  24. * fastdfs上传到服务器
  25. */
  26. public String saveFile(MultipartFile multipartFile) throws IOException {
  27. String[] fileAbsolutePath={};
  28. String fileName=multipartFile.getOriginalFilename();
  29. String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
  30. byte[] file_buff = null;
  31. InputStream inputStream=multipartFile.getInputStream();
  32. if(inputStream!=null){
  33. int len1 = inputStream.available();
  34. file_buff = new byte[len1];
  35. inputStream.read(file_buff);
  36. }
  37. inputStream.close();
  38. FastDFSFile file = new FastDFSFile(fileName, file_buff, ext);
  39. try {
  40. fileAbsolutePath = FastDFSClient.upload(file); //upload to fastdfs
  41. } catch (Exception e) {
  42. logger.error("upload file Exception!",e);
  43. }
  44. if (fileAbsolutePath==null) {
  45. logger.error("upload file failed,please upload again!");
  46. }
  47. String path=FastDFSClient.getTrackerUrl()+fileAbsolutePath[0]+ "/"+fileAbsolutePath[1];
  48. logger.info("文件上传成功,地址:"+path);
  49. return path;
  50. }
  51. }

访问:
FastDFS文件系统单机环境搭建 - 图8
访问成功:
FastDFS文件系统单机环境搭建 - 图9


结束语


至此我们的文件存储系统就搭建完成啦。