搭建FastDFS文件服务器

Posted 即使再小的帆也能远航!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了搭建FastDFS文件服务器相关的知识,希望对你有一定的参考价值。

介绍

FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件上传下载等,解决了大容量存储负载均衡的问题。特别适合以中小文件为载体的服务

注意:FastDFS只能上传小于500MB的文件

FastDFS系统有三个角色:跟踪服务器,存储服务器 ,客户端

跟踪服务器(Tracker Server):主要负责管理所有的Storage Server和group,每个Storage启动后会连接Tracker Server发送自己所属的group信息,并且保持周期性的心跳发送信息

存储服务器(Storage Server):主要提供存储以及备份服务,以group为单位,每个group有多台Storage Server,并且之间的数据相互同步备份

客户端(Client):主要发送上传和下载请求

存储策略

为了支持大容量,服务器采用了分卷或分组的组织方式。存储系统由一个或多个卷组成,卷与卷之间的文件是相互独立的,所有卷的文件容量累加就是整个存储系统中的文件容量

上传流程

1.Tracker Server和Storage Server建立连接,并定时检查group的信息

2.Client发送上传请求到Tracker Server

3.Tracker Server检查有没有可用的Storage,并返回可用的Storage

4.Client携带文件数据开始上传文件到Storage Server

5.Storage Server将文件写入磁盘,并返回文件的相对路径(group名+文件名)

6.Client接收相对路径并拼接服务器地址为完整的路径

FastDFS环境搭建

1.下载安装libfastcommon

libfastcommon是从 FastDFS 和 FastDHT 中提取出来的公共 C 函数库基础环境,只需安装即可

下载

https://github.com/happyfish100/libfastcommon/archive/V1.0.43.tar.gz

解压

tar -zxvf V1.0.43.tar.gz

编译、安装

cd libfastcommon-1.0.43
./make.sh && ./make.sh install

2.下载安装FastDFS

下载

https://github.com/happyfish100/fastdfs/archive/V6.06.tar.gz

解压

tar -zxvf V6.06.tar.gz

编译、安装

cd fastdfs-6.06
./make.sh && ./make.sh install

默认安装后的文件与目录

1.服务脚本

/etc/init.d/fdfs_storaged
/etc/init.d/fdfs_tracker

2.配置文件,这是作者给的样式文件,可以根据修改

/etc/fdfs/client.conf.sample
/etc/fdfs/storage.conf.sample
/etc/fdfs/tracker.conf.sample

3.命令工具,在/usr/bin目录下

fdfs_appender_test
fdfs_appender_test1
fdfs_append_file
fdfs_crc32
fdfs_delete_file
fdfs_download_file
fdfs_file_info
fdfs_monitor
fdfs_storaged		 #启动 storage server 
fdfs_test        #测试
fdfs_test1
fdfs_trackerd		 #启动tracker server
fdfs_upload_appender
fdfs_upload_file
stop.sh
restart.sh

3.配置Tracker Server

1.进入/etc/fdfs目录,复制跟踪服务器的样式文件tracker.conf.sample,重命名为tracker.conf

cp tracker.conf.sample tracker.conf

2.编辑tracker.conf,修改重要配置

vim tracker.conf
# 配置文件是否不生效,默认false为生效
disabled=false

# 提供服务的端口,默认
port=22122

# Tracker 数据和日志目录地址(根目录必须存在,子目录会自动创建)
base_path=/usr/local/fastdfs/tracker

# HTTP 服务端口
http.server_port=80

3.创建base_path

mkdir -p /usr/local/fastdfs/tracker

4.防火墙中打开tracker服务端口22122,安全组

#添加22122端口
firewall-cmd --permanent --zone=public --add-port=22122/tcp

#开启防火墙
systemctl start firewalld.service

#重启防火墙
sudo service firewalld restart

#查看端口列表
firewall-cmd --permanent --list-port

#禁用防火墙
systemctl stop firewalld

#查看防火墙状态
systemctl status firewalld

5.启动tracker

/etc/init.d/fdfs_trackerd start

6.查看是否启动成功

netstat -unltp|grep fdfs

7.设置tracker开启自动启动

chkconfig fdfs_trackerd on

8.tracker启动后,会在base_path下创建datalog目录,目录结构:

${base_path}
  |__data
  |   |__storage_groups.dat:存储分组信息
  |   |__storage_servers.dat:存储服务器列表
  |__logs
  |   |__trackerd.log: tracker server 日志文件 

4.配置Storage Server

1.进入 /etc/fdfs 目录,复制存储服务器的样式文件 storage.conf.sample,并重命名为 storage.conf

cp storage.conf.sample storage.conf

2.编辑storage.conf

vim storage.conf

# 配置文件是否不生效,false 为生效
disabled=false 

# 指定此 storage server 所在 组(卷)
group_name=group1

# storage server 服务端口
port=23000

# Storage 数据和日志目录地址(根目录必须存在,子目录会自动生成)
base_path=/usr/local/fastdfs/storage

# 存放文件时 storage server 支持多个路径。这里配置存放文件的基路径数目,通常只配一个目录。
store_path_count=1

# 逐一配置 store_path_count 个路径,索引号基于 0。
# 如果不配置 store_path0,那它就和 base_path 对应的路径一样。
store_path0=/usr/local/fastdfs/storage

# FastDFS 存储文件时,采用了两级目录。这里配置存放文件的目录个数。 
# 如果本参数只为 N(如: 256),那么 storage server 在初次运行时,会在 store_path 下自动创建 N * N 个存放文件的子目录。
subdir_count_per_path=256

# tracker_server 的列表 ,会主动连接 tracker_server
# 有多个 tracker server 时,每个 tracker server 写一行
tracker_server=127.0.0.1:22122
#tracker_server=127.0.0.1:22122

# 访问端口
http.server_port=80

3.创建base_path

mkdir -p /usr/local/fastdfs/storage

4.防火墙中打开tracker服务端口23000,安全组

#添加22122端口
firewall-cmd --permanent --zone=public --add-port=23000/tcp

#开启防火墙
systemctl start firewalld.service

#重启防火墙
sudo service firewalld restart

#查看防火墙端口列表
firewall-cmd --permanent --list-port

#禁用防火墙
systemctl stop firewalld

#查看防火墙状态
systemctl status firewalld

5.启动storage

/etc/init.d/fdfs_storaged start

查看是否启动成功

netstat -unltp|grep fdfs

查看storage是否和tracker连接成功

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

技术图片

6.设置tracker开启自动启动

chkconfig fdfs_storaged on

7.storage启动后,会在base_path下创建datalog目录,目录结构:

${base_path}
  |__data
			|_ N*N个目录
  |__logs
  |   |__trackerd.log: tracker server 日志文件 

5.文件上传测试

1.进入 /etc/fdfs 目录,复制客户端样式文件client.conf.sample,并重命名为 client.conf

cp client.conf.sample client.conf

2.编辑client.conf

vim client.conf
# Client 的数据和日志目录
base_path=/usr/local/fastdfs/client

# Tracker端口
tracker_server=127.0.0.1:22122

3.创建base_path

mkdir -p /usr/local/fastdfs/client

4.上传测试

/usr/bin/fdfs_upload_file /etc/fdfs/client.conf 文件路径

上传成功会返回相对路径,格式为group名/存储目录/一级目录/二级目录/文件名

技术图片

nginx环境搭建

安装Nginx所需的依赖

sudo yum -y install gcc

sudo yum -y install pcre-devel

sudo yum -y install zlib zlib-devel

sudo yum -y install openssl openssl-devel

下载nginx

wget https://nginx.org/download/nginx-1.10.3.tar.gz

解压

tar -zxvf nginx-1.10.3.tar.gz
cd cd nginx-1.10.3

配置

./configure

编译、安装

make && make install

进入安装后的目录,默认为/usr/local/nginx

cd /usr/local/nginx

启动nginx

cd sbin
./nginx

停止Nginx

./nginx stop

平滑重启nginx

./nginx -s reload

查看nginx版本及模块

./nginx -V

访问文件

修改nginx.conf

vim /usr/local/nginx/conf/nginx.conf

#添加如下行,将 /group1/M00 映射到 storage base_path目录
location /group1/M00 {
    alias /usr/local/fastdfs/storage/data;
}

# 重启nginx
/usr/local/nginx/sbin/nginx -s reload

访问之前上传的图片,在之前返回的相对路径前拼接服务器地址,这里的127.0.0.1是我模拟的

http://127.0.0.1/group1/M00/00/00/cH5Sbl65g1CAWd6JAAH553eRc5M827.jpg

FastDFS配置Nginx模块

下载

wget https://github.com/happyfish100/fastdfs-nginx-module/archive/V1.22.tar.gz

解压

tar -zxvf V1.22.tar.gz
cd fastdfs-nginx-module-1.22

配置Nginx

# 先停掉nginx服务
/usr/local/nginx/sbin/nginx -s stop

进入解压包目录
cd /softpackages/nginx-1.10.3/

# 添加模块 
./configure --add-module=../fastdfs-nginx-module-master/src

重新编译、安装
make && make install

查看nginx的模块

/usr/local/nginx/sbin/nginx -V

有这个说明添加模块成功

技术图片

复制fastdfs-nginx-module-master/src中的mod_fastdfs.conf/etc/fdfs目录,并修改

cp /fastdfs-nginx-module-master/src/mod_fastdfs.conf /etc/fdfs/

修改mod_fastdfs.conf

vim mod_fastdfs.conf

# Tracker Server
tracker_server=file.ljzsg.com:22122

#日志文件目录
base_path = /usr/local/fastdgs/tmp

# StorageServer 端口
storage_server_port=23000

# 如果文件ID的uri中包含/group**,则要设置为true
url_have_group_name = true

# Storage 配置的store_path0路径,必须和storage.conf中的一致
store_path0=/usr/local/fastdfs/storage

创建base_path

mkdir -p /usr/local/fastdgs/tmp

复制 FastDFS 的部分配置文件到/etc/fdfs 目录

cd /softpackages/fastdfs-6.06/conf/

cp anti-steal.jpg http.conf mime.types /etc/fdfs/

配置nginx,修改nginx.conf

vim /usr/local/nginx/conf/nginx.conf

在server节点的location添加fastdfs-nginx模块

server {
	 #与 /etc/fdfs/storage.conf 中的 http.server_port=80 (前面改成80了)相对应
	 listen       80;
	 server_name  localhost;
		
	 #如果上面修改了group_name,将group([0-9])改为配置的group_name
	 location ~/group([0-9])/M00 {
	     ngx_fastdfs_module;
	 }
}

启动nginx

/usr/local/nginx/sbin/nginx -t 
/usr/local/nginx/sbin/nginx

有该说明配置成功

技术图片

在地址栏访问http://127.0.0.1/group1/M00/00/00/cH5Sbl65g1CAWd6JAAH553eRc5M827

Java集成

fastdfs-client

1.导入fastdfs依赖到pom.xml

<!--文件上传-->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>
<!--fastdfs-->
<dependency>
    <groupId>com.github.tobato</groupId>
    <artifactId>fastdfs-client</artifactId>
    <version>1.27.2</version>
</dependency>

2.application.yml配置fastdfs

fdfs:
  connect-timeout: 30 		 #连接超时时间 秒
  so-timeout: 		 30      #读取超时时间 秒
  tracker-list: 192.168.169.250:22122 #tracker server的ip和端口

file:
  host: http://192.168.169.250/ #文件host路径

3.开始上传

FastFileStorageClient:FastDFS提供的客户端类,用于上传下载文件

/**
  * 上传一般文件
  *
  * @param inputStream 文件输入流
  * @param fileSize		 文件大小
  * @param fileExtName 文件后缀名
  * @param metaDataSet 元数据集
  * @return
  */
 StorePath uploadFile(InputStream inputStream, long fileSize, String fileExtName, Set<MetaData> metaDataSet);

StorePath:上传文件后返回的类,用于获取路径

getFullPath(); //获取全路径
storePath.getGroup(); //获取group_name
storePath.getPath();  //获取路径  

/**
 * @Desc: 文件上传
 */
@RestController
public class FileController {
	
  	//注入文件host路径
    @Value("${file.host}")
    String fileHost;

  	//fastdfs提供的客户端
    @Autowired
    FastFileStorageClient storageClient;

    @PostMapping("/upload")
    public String upload(@RequestParam("file") MultipartFile file) {
        String fullPath = "";
        if (file != null) {
          	//获取文件名称  xxx.jpg
            String fileName = file.getOriginalFilename();
            if (StringUtils.isNotBlank(fileName)) {
                //获取文件的后缀名
                String[] splitFileName = fileName.split("\.");
                String fileExtName = splitFileName[splitFileName.length - 1];
              	
                try {
                    StorePath storePath = storageClient.uploadFile(file.getInputStream(), file.getSize(), fileExtName, null);
                    fullPath = storePath.getFullPath();
                    // group1/M00/00/00/wKip-l66eNiACV7JAAGqkGAODus33.jpeg
                    System.out.println("File Path === " + fullPath);
                    // 将图片存储数据库...
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
      	//返回最终路径  http://192.168.169.250/group1/M00/00/00/wKip-l66eNiACV7JAAGqkGAODus33.jpeg
        return fileHost + fullPath;
    }
}


fastdfs-client-java

1.导入fastdfs-client-java依赖到pom.xml

<dependency>
      <groupId>net.oschina.zcx7878</groupId>
      <artifactId>fastdfs-client-java</artifactId>
      <version>1.27.0.0</version>
</dependency>

2.工具类copy到项目中

/**
 * FastDFS工具类【实现文件上传、下载、删除、查询】
 *
 * @author Zhangyongliang
 */
public class FastDFSClient {
    private TrackerClient trackerClient = null;
    private TrackerServer trackerServer = null;
    private StorageServer storageServer = null;
    private StorageClient1 storageClient = null;

    public FastDFSClient(String conf) throws Exception {
        if (conf.contains("classpath:")) {
            String path = URLDecoder.decode(getClass().getProtectionDomain().getCodeSource().getLocation().toString(), "UTF-8");
            path = path.substring(6);
            conf = conf.replace("classpath:", URLDecoder.decode(path, "UTF-8"));
        }
        ClientGlobal.init(conf);
        trackerClient = new TrackerClient();
        trackerServer = trackerClient.getConnection();
        storageServer = null;
        storageClient = new StorageClient1(trackerServer, storageServer);
    }


    /**
     * 上传文件方法
     * <p>Title: uploadFile</p>
     * <p>Description: </p>
     *
     * @param fileName 文件全路径
     * @param extName  文件扩展名,不包含(.)
     * @param metas    文件扩展信息
     * @return
     * @throws Exception
     */
    public String uploadFile(String fileName, String extName, NameValuePair[] metas) {
        String result = null;
        try {
            result = storageClient.upload_file1(fileName, extName, metas);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (MyException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 上传文件,传fileName
     *
     * @param fileName 文件的磁盘路径名称 如:D:/image/aaa.jpg
     * @return null为失败
     */
    public String uploadFile(String fileName) {
        return uploadFile(fileName, null, null);
    }

    /**
     * @param fileName 文件的磁盘路径名称 如:D:/image/aaa.jpg
     * @param extName  文件的扩展名 如 txt jpg等
     * @return null为失败
     */
    public String uploadFile(String fileName, String extName) {
        return uploadFile(fileName, extName, null);
    }

    /**
     * 上传文件方法
     * <p>Title: uploadFile</p>
     * <p>Description: </p>
     *
     * @param fileContent 文件的内容,字节数组
     * @param extName     文件扩展名
     * @param metas       文件扩展信息
     * @return
     * @throws Exception
     */
    public String uploadFile(byte[] fileContent, String extName, NameValuePair[] metas) {
        String result = null;
        try {
            result = storageClient.upload_file1(fileContent, extName, metas);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (MyException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 上传文件
     *
     * @param fileContent 文件的字节数组
     * @return null为失败
     * @throws Exception
     */
    public String uploadFile(byte[] fileContent) throws Exception {
        return uploadFile(fileContent, null, null);
    }

    /**
     * 上传文件
     *
     * @param fileContent 文件的字节数组
     * @param extName     文件的扩展名 如 txt  jpg png 等
     * @return null为失败
     */
    public String uploadFile(byte[] fileContent, String extName) {
        return uploadFile(fileContent, extName, null);
    }

    /**
     * 文件下载到磁盘
     *
     * @param path   图片路径
     * @param output 输出流 中包含要输出到磁盘的路径
     * @return -1失败,0成功
     */
    public int download_file(String path, BufferedOutputStream output) {
        int result = -1;
        try {
            byte[] b = storageClient.download_file1(path);
            try {
                if (b != null) {
                    output.write(b);
                    result = 0;
                }
            } catch (Exception e) {
            } //用户可能取消了下载
            finally {
                if (output != null) {
                    try {
                        output.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 获取文件数组
     *
     * @param path 文件的路径 如group1/M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg
     * @return
     */
    public byte[] download_bytes(String path) {
        byte[] b = null;
        try {
            b = storageClient.download_file1(path);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (MyException e) {
            e.printStackTrace();
        }
        return b;
    }

    /**
     * 删除文件
     *
     * @param group       组名 如:group1
     * @param storagePath 不带组名的路径名称 如:M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg
     * @return -1失败,0成功
     */
    public Integer delete_file(String group, String storagePath) {
        int result = -1;
        try {
            result = storageClient.delete_file(group, storagePath);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (MyException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * @param storagePath 文件的全部路径 如:group1/M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg
     * @return -1失败,0成功
     * @throws IOException
     * @throws Exception
     */
    public Integer delete_file(String storagePath) {
        int result = -1;
        try {
            result = storageClient.delete_file1(storagePath);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (MyException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 获取远程服务器文件资源信息
     *
     * @param groupName      文件组名 如:group1
     * @param remoteFileName M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg
     * @return
     */
    public FileInfo getFile(String groupName, String remoteFileName) {
        try {
            return storageClient.get_file_info(groupName, remoteFileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

3.在resource目录下创建fdfs_client.properties配置文件

connect_timeout = 2
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 8088   # tracker Http端口
http.anti_steal_token = no      # 暂无作用
http.secret_key = FastDFS1234567890     # 暂无作用
tracker_server = 192.168.43.60:22122    # tracker Server地址信息

4.使用工具类上传

 /**
 * @Desc: 文件上传
 */
@RestController
public class FileController {
	
  	//注入文件host路径
    @Value("${file.host}")
    String fileHost;

    @PostMapping("/upload")
    public String uploadFast(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws Exception {
        //初始化全局配置。加载一个配置文件。
        String confUrl = this.getClass().getClassLoader().getResource("fdfs_client.properties").getPath();
        FastDFSClient fastDFSClient = new FastDFSClient(confUrl);
        String filePath = "";
        if (file != null) {
            String fileName = file.getOriginalFilename();
            if (fileName != null || !"".equals(fileName)) {
                //文件的后缀名
                String[] splitFileName = fileName.split("\.");
                String fileExtName = splitFileName[splitFileName.length - 1];
              	//开始上传,返回文件路径 group/M00/00/00/xxxxx.xx
                filePath = fastDFSClient.uploadFile(file.getBytes(), fileExtName, null);
            }
            //省略其他
        }
       return fileHost + filePath;
	}
}

以上是关于搭建FastDFS文件服务器的主要内容,如果未能解决你的问题,请参考以下文章

Docker 搭建FastDFS文件系统

搭建FastDFS文件服务器

搭建FastDFS文件服务器

Linux(Ubuntu)搭建FastDFS文件管理系统

搭建FastDFS

分布式搭建-简易版文件上传下载服务器FastDFS