【视频点播系统】FastDFS-SDK 介绍及使用
FastDFS-SDK 介绍及使用
一. FastDFS 介绍
- FastDFS 是一个开源的分布式文件存储系统,主要用于存储图片、视频等大容量或海量小文件。它采用 Tracker + Storage 的架构设计。
- Tracker 是调度与管理节点,负责管理所有 Storage 的状态信息,包括节点存活情况、分组信息等。Tracker 本身不存储文件,也不参与文件传输,主要作用是为客户端分配合适的 Storage 节点。Tracker 无状态,可部署多个实例,提高系统的可用性。
- Storage 是实际的存储节点,负责文件的上传、下载和数据保存。Storage 按 Group 进行组织,同一 Group 内的多个 Storage 节点之间会进行数据同步,实现数据冗余和高可用;不同 Group 之间的数据相互独立,用于系统扩展。
- 客户端在上传或下载文件时,首先向 Tracker 查询可用的 Storage,然后直接与 Storage 进行数据传输,从而实现高性能的分布式文件存储。

FastDFS 特点:
- 高性能:采用 Tracker 与 Storage 分离的架构,客户端直接与 Storage 节点进行数据传输,减少中间环节,适合高并发读写场景。
- 高可用性:通过 Group 机制实现数据冗余,同一组内的多个 Storage 节点之间自动同步数据,单个节点故障不会影响整体服务。
- 良好的扩展性:支持水平扩展,可通过增加 Storage 组或节点来提升存储容量和访问能力,无需停机。
- 轻量且易部署:架构简单、依赖少,部署和运维成本低,适合中小规模分布式存储系统。
- 无中心化存储节点:Tracker 不存储文件,也不参与数据传输,避免性能瓶颈,系统更稳定。
- 支持多种客户端:提供多语言客户端接口,方便与业务系统集成。
- 常与 Nginx 结合使用:可通过 FastDFS + Nginx 提供 HTTP 方式的文件访问,适合图片、视频等资源服务场景。
二. FastDFS 安装
# 1. 更新软件源
sudo apt update
# 2. 安装依赖
sudo apt install -y gcc g++ make libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev git
# 3. 安装 libfastcommon
cd /usr/local/src
sudo git clone https://github.com/happyfish100/libfastcommon.git
cd libfastcommon
sudo ./make.sh
sudo ./make.sh install
# 4. 安装 FastDFS
cd /usr/local/src
sudo git clone https://github.com/happyfish100/fastdfs.git
cd fastdfs
sudo ./make.sh
sudo ./make.sh install
# 5. 拷贝配置文件
sudo cp /etc/fdfs/tracker.conf.sample /etc/fdfs/tracker.conf
sudo cp /etc/fdfs/storage.conf.sample /etc/fdfs/storage.conf
sudo cp /etc/fdfs/client.conf.sample /etc/fdfs/client.conf
# 6. 创建数据目录
sudo mkdir -p /data/fastdfs/tracker
sudo mkdir -p /data/fastdfs/storage
# 7. 配置 Tracker
sudo sed -i 's|^disabled=true|disabled=false|' /etc/fdfs/tracker.conf
sudo sed -i 's|^base_path=.*|base_path=/data/fastdfs/tracker|' /etc/fdfs/tracker.conf
# 8. 启动 Tracker 服务
sudo fdfs_trackerd /etc/fdfs/tracker.conf start
# 9. 配置 Storage
sudo sed -i 's|^disabled=true|disabled=false|' /etc/fdfs/storage.conf
sudo sed -i 's|^base_path=.*|base_path=/data/fastdfs/storage|' /etc/fdfs/storage.conf
sudo sed -i 's|^store_path0=.*|store_path0=/data/fastdfs/storage|' /etc/fdfs/storage.conf
sudo sed -i 's|^tracker_server=.*|tracker_server=127.0.0.1:22122|' /etc/fdfs/storage.conf
# 10. 启动 Storage 服务
sudo fdfs_storaged /etc/fdfs/storage.conf start
由于在环境搭建中已经安装了 FastDFS 服务器,并且启动了:

三. fastdfs SDK 介绍
fastdfs 是 FastDFS 分布式文件系统为 C/C++ 程序提供的官方客户端开发库,主要用于实现文件的上传、下载、删除及元数据查询等功能。该客户端通过与 Tracker 和 Storage 服务器通信,完成文件定位与存储管理,具有高性能、低开销的特点,适合对效率要求较高的业务场景。fastdfs 客户端封装了底层网络通信和协议细节,开发者只需调用相关 API 即可快速集成分布式文件存储能力。它广泛应用于图片服务器、日志存储、音视频文件管理等场景,支持大规模文件并发访问,具备良好的可扩展性和稳定性,是使用 FastDFS 的核心基础组件之一。
四. fastdfs SDK 安装
fastdfs 的项目地址为:https://github.com/happyfish100/fastdfs.git
# 1. 安装 libfastcommon
git clone https://github.com/happyfish100/libfastcommon.git --depth 1
cd libfastcommon
./make.sh
sudo ./make.sh install
cd ..
# 2. 安装 libserverframe
git clone https://github.com/happyfish100/libserverframe.git --depth 1
cd libserverframe
./make.sh
sudo ./make.sh install
cd ..
# 3. 编译并安装 FastDFS
git clone https://github.com/happyfish100/fastdfs.git --depth 1
cd fastdfs
./make.sh
sudo ./make.sh install
# 4. 复制配置文件(供 Nginx 访问使用)
sudo cp conf/http.conf /etc/fdfs/
sudo cp conf/mime.types /etc/fdfs/
由于在环境搭建中已经安装了 fastdfs,这里不需要再次安装了。
五. fastdfs 类与接口
1. 日志、错误信息、客户端配置
fastdfs 有自己的日志输出模块 (无法替换成为我们封装的 spdlog 日志输出,除非去修改 fastdfs 的源代码,将所有的日志输出操作进行替换),这里介绍 fastdfs 的日志相关操作,主要是为了让 fastdfs 只输出错误日志,避免因为大量的第三方库日志输出影响我们的视线。
Error 用于获取 fastdfs 接口调用失败的错误原因接口。
// 全局日志上下文
typedef struct log_context {
int log_level; // 日志级别
int log_fd; // 日志文件描述符
//.....
};
// 全局日志上下文实例
extern LogContext g_log_context;
// 初始化全局日志上下文
int log_init();
// 忽略SIGPIPE信号
int ignore_signal_pipe();
// 定义一个宏,用于获取错误码对应的错误信息字符串
#define STRERROR(no) (strerror(no) != NULL ? strerror(no) : "Unkown error")
初始化客户端全局配置,默认可以通过配置文件进行初始化,其实也可以将配置信息整到内存中,通过内存数据进行客户端全局配置初始化。
connect_timeout = 5 // 连接超时时间,单位秒
network_timeout = 60 // 网络超时时间,单位秒
tracker_server = 192.168.174.128:22122 // Tracker服务器地址和端口
use_connection_pool = true // 是否使用连接池
connection_pool_max_idle_time = 3600 // 连接池最大空闲时间,单位秒
// 从配置文件初始化客户端配置
#define fdfs_client_init(filename) fdfs_client_init_ex((&g_tracker_group), filename)
int fdfs_client_init_ex(TrackerServerGroup *pTrackerGroup, const char *conf_filename);
// 根据给定内存数据初始化客户端配置
#define fdfs_client_init_from_buffer(buffer) fdfs_client_init_from_buffer_ex((&g_tracker_group), buffer)
int fdfs_client_init_from_buffer_ex(TrackerServerGroup *pTrackerGroup, const char *buffer);
// 销毁客户端配置
#define fdfs_client_destroy() fdfs_client_destroy_ex((&g_tracker_group))
void fdfs_client_destroy_ex(TrackerServerGroup *pTrackerGroup);
2. Tracker、Storage
获取 Tracker 句柄,用于访问 tracker 服务器。
typedef struct {
int sock; // 套接字描述符
uint16_t port; // 端口号
short af; // 地址族
FCCommunicationType comm_type; // 通信类型
bool validate_flag; // 验证标志
char ip_addr[IP_ADDRESS_SIZE]; // IP地址
void *arg1; // 自定义参数1
char args[0]; // 自定义参数2
} ConnectionInfo;
// 从连接池获取一个tracker连接
#define tracker_get_connection() tracker_get_connection_ex((&g_tracker_group))
ConnectionInfo *tracker_get_connection_ex(TrackerServerGroup *pTrackerGroup);
// 关闭连接:关闭指定的tracker连接
#define tracker_close_connection(pTrackerServer) tracker_close_connection_ex(pTrackerServer, false)
void tracker_close_connection_ex(ConnectionInfo *conn, const bool bForceClose);
从 Tracker 服务器上获取 Storage 信息,并返回 storage 节点操作句柄。
#define FDFS_GROUP_NAME_MAX_LEN 16
// 查询存储节点的存储路径索引
// pTrackerServer:tracker服务器连接信息
// pStorageServer:存储服务器连接信息
// group_name:文件组名
// store_path_index:存储路径索引
#define tracker_query_storage_store(pTrackerServer, pStorageServer, group_name, store_path_index) \
tracker_query_storage_store_without_group(pTrackerServer, pStorageServer, group_name, store_path_index)
int tracker_query_storage_store_without_group(ConnectionInfo *pTrackerServer,
ConnectionInfo *pStorageServer, char *group_name, int *store_path_index);
3. 文件上传、下载、删除
文件上传相关接口:
// 上传文件到指定的storage节点
// pTrackerServer: tracker 连接信息
// pStorageServer: storage 连接信息,可以为 nullptr
// store_path_index: 获取storage节点时顺便获取的索引, 若没有主动获取节点则可以为0
// cmd: 上传文件命令,默认值为STORAGE_PROTO_CMD_UPLOAD_FILE
// local_filename: 本地要上传的文件路径名
// file_ext_name: 文件扩展名,可以为 nullptr
// meta_list: 文件元信息数组
// meta_count: 元信息元素数量,若没有传递元信息数组则设置为0
// group_name: 若没有主动获取storage节点,则可以设置为 nullptr
// file_id: 输出参数--是文件上传保存成功后分配的标识ID
int storage_upload_by_filename1_ex(ConnectionInfo *pTrackerServer, \
ConnectionInfo *pStorageServer, const int store_path_index, \
const char cmd, const char *local_filename, \
const char *file_ext_name, const FDFSMetaData *meta_list, \
const int meta_count, const char *group_name, char *file_id);
// 上传文件内容到指定的storage节点
// pTrackerServer: tracker 连接信息
// pStorageServer: storage 连接信息,可以为 nullptr
// store_path_index: 获取storage节点时顺便获取的索引, 若没有主动获取节点则可以为0
// cmd: 上传文件命令,默认值为STORAGE_PROTO_CMD_UPLOAD_FILE
// upload_type: 上传文件类型
// file_buff: 文件内容指针
// arg: 上传文件类型为FDFS_UPLOAD_BY_BUFF时,传递的参数指针,其他类型时可以为 nullptr
// file_size: 文件内容大小
// file_ext_name: 文件扩展名,可以为 nullptr
// meta_list: 文件元信息数组
// meta_count: 元信息元素数量,若没有传递元信息数组则设置为0
// group_name: 若没有主动获取storage节点,则可以设置为 nullptr
// file_id: 输出参数--是文件上传保存成功后分配的标识ID
int storage_do_upload_file1(ConnectionInfo *pTrackerServer, \
ConnectionInfo *pStorageServer, const int store_path_index, \
const char cmd, const int upload_type, \
const char *file_buff, void *arg, const int64_t file_size, \
const char *file_ext_name, const FDFSMetaData *meta_list, \
const int meta_count, const char *group_name, char *file_id);
// 宏定义
#define storage_upload_by_filename1(pTrackerServer, pStorageServer, \
store_path_index, local_filename, file_ext_name, \
meta_list, meta_count, group_name, file_id) \
storage_upload_by_filename1_ex(pTrackerServer, pStorageServer, \
store_path_index, STORAGE_PROTO_CMD_UPLOAD_FILE, \
local_filename, file_ext_name, meta_list, meta_count, \
group_name, file_id)
#define storage_upload_by_filebuff1(pTrackerServer, pStorageServer, \
store_path_index, file_buff, file_size, file_ext_name, \
meta_list, meta_count, group_name, file_id) \
storage_do_upload_file1(pTrackerServer, pStorageServer, \
store_path_index, STORAGE_PROTO_CMD_UPLOAD_FILE, \
FDFS_UPLOAD_BY_BUFF, file_buff, NULL, \
file_size, file_ext_name, meta_list, meta_count, \
group_name, file_id)
// 文件元信息配置结构
typedef struct {
char name[FDFS_MAX_META_NAME_LEN + 1]; // 元信息键
char value[FDFS_MAX_META_VALUE_LEN + 1]; // 元信息值
} FDFSMetaData;
// 获取文件元信息
int storage_get_metadata1(ConnectionInfo *pTrackerServer, ConnectionInfo *pStorageServer,
const char *file_id, FDFSMetaData **meta_list, int *meta_count);
文件下载相关接口:
// 从Storage下载文件到内存
// pTrackerServer: tracker 连接信息
// pStorageServer: storage 连接信息
// download_type: 下载类型,FDFS_DOWNLOAD_TO_BUFF或FDFS_DOWNLOAD_TO_FILE
// file_id: 文件ID
// file_offset: 文件偏移量
// download_bytes: 下载字节数
// file_buff: 文件缓冲区
// arg: 回调函数参数
// file_size: 文件大小
int storage_do_download_file1_ex(ConnectionInfo *pTrackerServer, ConnectionInfo *pStorageServer, \
const int download_type, const char *file_id, const int64_t file_offset, const int64_t download_bytes, \
char **file_buff, void *arg, int64_t *file_size);
// 从Storage下载文件到本地文件
// pTrackerServer: tracker 连接信息
// pStorageServer: storage 连接信息
// file_id: 文件ID
// local_filename: 本地文件名
// file_size: 文件大小
int storage_download_file_to_file1(ConnectionInfo *pTrackerServer, ConnectionInfo *pStorageServer, \
const char *file_id, const char *local_filename, int64_t *file_size);
// 宏定义
#define storage_download_file1(pTrackerServer, pStorageServer, file_id, file_buff, file_size) \
torage_do_download_file1_ex(pTrackerServer, pStorageServer, FDFS_DOWNLOAD_TO_BUFF, file_id, 0, 0, file_buff, NULL, file_size)
#define storage_download_file_to_buff1(pTrackerServer, pStorageServer, file_id, file_buff, file_size) \
storage_do_download_file1_ex(pTrackerServer, pStorageServer, FDFS_DOWNLOAD_TO_BUFF, file_id, 0, 0, file_buff, NULL, file_size)
文件删除接口:
// 删除Storage文件
// pTrackerServer: tracker 连接信息
// pStorageServer: storage 连接信息
// file_id: 文件ID
int storage_delete_file1(ConnectionInfo *pTrackerServer, ConnectionInfo *pStorageServer, const char *file_id);
六. fastdfs 使用样例
1. 目录结构
example/
|-- fastdfs
| |-- client.conf
| |-- download.cc
| |-- makefile
| |-- remove.cc
| |-- upload.cc
2. 项目构建
# makefile
all: upload download remove
upload: upload.cc
g++ -o upload upload.cc -lfastcommon -lfdfsclient
download: download.cc
g++ -o download download.cc -lfastcommon -lfdfsclient
remove: remove.cc
g++ -o remove remove.cc -lfastcommon -lfdfsclient
.PHONY: clean
clean:
rm -f upload download remove
编译生成可执行文件:

3. 代码实现
3.1 配置文件
# client.conf
connect_timeout = 5 // 连接超时时间,单位秒
network_timeout = 60 // 网络超时时间,单位秒
tracker_server = 192.168.174.128:22122 // Tracker服务器地址和端口
use_connection_pool = true // 是否使用连接池
connection_pool_max_idle_time = 3600 // 连接池最大空闲时间,单位秒
3.2 文件上传
// upload.cc
#include <iostream>
#include <fastcommon/logger.h>
#include <fastdfs/fdfs_client.h>
int main(int argc, char* argv[])
{
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " file_path" << std::endl;
return -1;
}
// 1.初始化日志输出模块
g_log_context.log_level = LOG_ERR;
log_init();
// 2.初始化FastDFS客户端全局配置
int ret = fdfs_client_init("./client.conf");
if (ret != 0) {
std::cout << "初始化FastDFS客户端全局配置失败:" << STRERROR(ret) << std::endl;
return -1;
}
// 3.获取tracker服务器连接句柄
ConnectionInfo* tracker_server = tracker_get_connection();
if (tracker_server == nullptr) {
std::cout << "获取tracker服务器连接句柄失败" << std::endl;
return -1;
}
// 4.上传文件到FastDFS
char file_id[256];
ret = storage_upload_by_filename1(tracker_server, nullptr, 0, argv[1], nullptr, nullptr, 0, nullptr, file_id);
if (ret != 0) {
std::cout << "上传文件到FastDFS失败:" << STRERROR(ret) << std::endl;
return -1;
}
std::cout << "上传文件到FastDFS成功:" << file_id << std::endl;
// 5.释放资源:关闭tracker服务器连接句柄和FastDFS客户端全局配置
tracker_close_connection(tracker_server);
fdfs_client_destroy();
return 0;
}
上传 makefile 文件到 FastDFS 服务器中,返回文件 ID:

3.3 文件下载
// download.cc
#include <iostream>
#include <fastcommon/logger.h>
#include <fastdfs/fdfs_client.h>
int main(int argc, char* argv[])
{
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " file_id" << std::endl;
return -1;
}
// 1.初始化日志输出模块
g_log_context.log_level = LOG_ERR;
log_init();
// 2.初始化FastDFS客户端全局配置
int ret = fdfs_client_init("./client.conf");
if (ret != 0) {
std::cout << "初始化FastDFS客户端全局配置失败:" << STRERROR(ret) << std::endl;
return -1;
}
// 3.获取tracker服务器连接句柄
ConnectionInfo* tracker_server = tracker_get_connection();
if (tracker_server == nullptr) {
std::cout << "获取tracker服务器连接句柄失败" << std::endl;
return -1;
}
// 4.下载文件到本地
int64_t file_size;
ret = storage_download_file_to_file1(tracker_server, nullptr, argv[1], "./makefile.back", &file_size);
if (ret != 0) {
std::cout << "下载文件失败:" << STRERROR(ret) << std::endl;
return -1;
}
std::cout << "下载文件成功:" << file_size << std::endl;
// 5.释放资源:关闭tracker服务器连接句柄和FastDFS客户端全局配置
tracker_close_connection(tracker_server);
fdfs_client_destroy();
return 0;
}
根据 FastDFS 服务器中的文件 ID,下载文件到本地:

3.4 文件删除
// remove.cc
#include <iostream>
#include <fastcommon/logger.h>
#include <fastdfs/fdfs_client.h>
int main(int argc, char* argv[])
{
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " file_id" << std::endl;
return -1;
}
// 1.初始化日志输出模块
g_log_context.log_level = LOG_ERR;
log_init();
// 2.初始化FastDFS客户端全局配置
int ret = fdfs_client_init("./client.conf");
if (ret != 0) {
std::cout << "初始化FastDFS客户端全局配置失败:" << STRERROR(ret) << std::endl;
return -1;
}
// 3.获取tracker服务器连接句柄
ConnectionInfo* tracker_server = tracker_get_connection();
if (tracker_server == nullptr) {
std::cout << "获取tracker服务器连接句柄失败" << std::endl;
return -1;
}
// 4.删除FastDFS文件
ret = storage_delete_file1(tracker_server, nullptr, argv[1]);
if (ret != 0) {
std::cout << "删除FastDFS文件失败:" << STRERROR(ret) << std::endl;
return -1;
}
std::cout << "删除FastDFS文件成功:" << argv[1] << std::endl;
// 5.释放资源:关闭tracker服务器连接句柄和FastDFS客户端全局配置
tracker_close_connection(tracker_server);
fdfs_client_destroy();
return 0;
}
根据文件 ID,删除FastDFS 服务器中的文件,顺便删除本地下载的 makefile.back 文件:

七. fastdfs 封装
1. 设计与实现
FDFS 这边封装思想比较简单,根据提供的 API 接口封装一个 FDFS 客户端类,将初始化和请求过程封装起来,向外提供文件上传,下载,删除的接口即可 (上传和下载接口都需要提供两种,基于缓冲区和文件的操作,因为网络通信中很多时候都是针对收到的数据进行操作,完全没必要把数据先存文件再上传)
除此之外,为了便于初始化客户端,因此定义一个客户端配置信息结构体,只需要包含一些关键配置信息即可。
1.1 目录结构
source/
|-- fdfs.cc
|-- fdfs.h
1.2 代码实现
// fdfs.h
#pragma once
#include <iostream>
#include <vector>
#include <optional>
extern "C" {
#include <fastdfs/fdfs_client.h>
#include <fastcommon/logger.h>
}
namespace xzyfdfs {
// FastDFS客户端全局配置
struct fdfs_settings {
std::vector<std::string> tracker_servers; // Tracker服务器地址和端口列表
int connect_timeout = 30; // 连接超时时间,单位秒
int network_timeout = 30; // 网络超时时间,单位秒
bool use_connection_pool = true; // 是否使用连接池:多线程环境下开启,保证上传、下载和删除文件等操作的线程安全
int connection_pool_max_idle_time = 3600; // 连接池最大空闲时间,单位秒
};
// FastDFS客户端类
class FDFSClient {
public:
FDFSClient();
~FDFSClient();
// 初始化FastDFS客户端
static void init(const fdfs_settings& settings);
// 销毁FastDFS客户端
static void destroy();
// 上传文件到FastDFS
static std::optional<std::string> upload_from_buff(const std::string& buff);
// 从本地文件上传文件到FastDFS
static std::optional<std::string> upload_from_file(const std::string& path);
// 下载文件到内存缓冲区
static bool download_to_buff(const std::string& file_id, std::string& buff);
// 从FastDFS下载文件到本地文件
static bool download_to_file(const std::string& file_id, const std::string& path);
// 删除FastDFS文件
static bool remove(const std::string& file_id);
};
}
// fdfs.cc
#include <sstream>
#include "log.h"
#include "fdfs.h"
namespace xzyfdfs {
FDFSClient::FDFSClient(){}
FDFSClient::~FDFSClient(){}
// 初始化FastDFS客户端
void FDFSClient::init(const fdfs_settings& settings) {
// 1.初始化日志输出模块
g_log_context.log_level = LOG_ERR;
log_init();
// 2.针对settings中的客户端配置信息组成一个字符串
std::stringstream ss;
for (auto& tracker : settings.tracker_servers) {
ss << "tracker_server = " << tracker << "\n";
}
ss << "connect_timeout = " << settings.connect_timeout << "\n";
ss << "network_timeout = " << settings.network_timeout << "\n";
ss << "use_connection_pool = " << settings.use_connection_pool << "\n";
ss << "connection_pool_max_idle_time = " << settings.connection_pool_max_idle_time << "\n";
// 3.初始化FastDFS客户端配置
int ret = fdfs_client_init_from_buffer(ss.str().c_str());
if (ret != 0) {
ERR("初始化客户端配置失败:{}", STRERROR(ret));
abort(); // 异常退出
}
}
// 销毁FastDFS客户端
void FDFSClient::destroy() {
fdfs_client_destroy();
}
// 上传文件到FastDFS
std::optional<std::string> FDFSClient::upload_from_buff(const std::string& buff) {
// 1.获取tracker服务器连接句柄
ConnectionInfo* tracker_server = tracker_get_connection();
if (tracker_server == nullptr) {
ERR("获取tracker服务器连接句柄失败");
return std::optional<std::string>();
}
// 2.上传文件到FastDFS
char file_id[256];
int ret = storage_upload_by_filebuff1(tracker_server, nullptr, 0, buff.c_str(), buff.size(), nullptr, nullptr, 0, nullptr, file_id);
if (ret != 0) {
ERR("上传文件到FastDFS失败:{}", STRERROR(ret));
return std::optional<std::string>();
}
// 3.销毁tracker服务器连接句柄
tracker_close_connection(tracker_server);
return std::string(file_id);
}
// 本地文件上传文件到FastDFS
std::optional<std::string> FDFSClient::upload_from_file(const std::string& path) {
// 1.获取tracker服务器连接句柄
ConnectionInfo* tracker_server = tracker_get_connection();
if (tracker_server == nullptr) {
ERR("获取tracker服务器连接句柄失败");
return std::optional<std::string>();
}
// 2.本地文件上传文件到FastDFS
char file_id[256];
int ret = storage_upload_by_filename1(tracker_server, nullptr, 0, path.c_str(), nullptr, nullptr, 0, nullptr, file_id);
if (ret != 0) {
ERR("本地文件上传文件到FastDFS失败:{}", STRERROR(ret));
return std::optional<std::string>();
}
// 3.销毁tracker服务器连接句柄
tracker_close_connection(tracker_server);
return std::string(file_id);
}
// 下载文件到内存缓冲区
bool FDFSClient::download_to_buff(const std::string& file_id, std::string& buff) {
// 1.获取tracker服务器连接句柄
ConnectionInfo* tracker_server = tracker_get_connection();
if (tracker_server == nullptr) {
ERR("获取tracker服务器连接句柄失败");
return false;
}
// 2.下载文件到内存缓冲区
int64_t file_size;
char* file_buff = nullptr;
int ret = storage_download_file1(tracker_server, nullptr, file_id.c_str(), &file_buff, &file_size);
if (ret != 0) {
ERR("下载文件到内存缓冲区失败:{}", STRERROR(ret));
return false;
}
buff.assign(file_buff, file_size); // 复制文件内容到buff
free(file_buff); // 释放文件内容内存
// 3.销毁tracker服务器连接句柄
tracker_close_connection(tracker_server);
return true;
}
// 从FastDFS下载文件到本地文件
bool FDFSClient::download_to_file(const std::string& file_id, const std::string& path) {
// 1.获取tracker服务器连接句柄
ConnectionInfo* tracker_server = tracker_get_connection();
if (tracker_server == nullptr) {
ERR("获取tracker服务器连接句柄失败");
return false;
}
// 2.从FastDFS下载文件到本地文件
int64_t file_size;
int ret = storage_download_file_to_file1(tracker_server, nullptr, file_id.c_str(), path.c_str(), &file_size);
if (ret != 0) {
ERR("从FastDFS下载文件到本地文件失败:{}", STRERROR(ret));
return false;
}
// 3.销毁tracker服务器连接句柄
tracker_close_connection(tracker_server);
return true;
}
// 删除FastDFS文件
bool FDFSClient::remove(const std::string& file_id) {
// 1.获取tracker服务器连接句柄
ConnectionInfo* tracker_server = tracker_get_connection();
if (tracker_server == nullptr) {
ERR("获取tracker服务器连接句柄失败");
return false;
}
// 2.删除FastDFS文件
int ret = storage_delete_file1(tracker_server, nullptr, file_id.c_str());
if (ret != 0) {
ERR("删除FastDFS文件失败:{}", STRERROR(ret));
return false;
}
// 3.销毁tracker服务器连接句柄
tracker_close_connection(tracker_server);
return true;
}
}
2. 使用样例
1.1 目录结构
test/
|-- fastdfs
| |-- fdfs_test
| |-- fdfs_test.cc
| |-- makefile
1.2 项目构建
# makefile
fdfs_test: fdfs_test.cc ../../source/log.cc ../../source/util.cc ../../source/fdfs.cc
g++ -o $@ $^ -std=c++17 -lfmt -lspdlog -ljsoncpp -lfastcommon -lfdfsclient -lpthread
.PHONY: clean
clean:
rm -f fdfs_test
编译生成可执行程序:

1.3 代码实现
// fdfs_test.cc
#include "../../source/log.h"
#include "../../source/util.h"
#include "../../source/fdfs.h"
// 基于文件的上传下载测试
std::string file_test() {
// 1.将文件上传至FastDFS服务器
std::optional<std::string> id = xzyfdfs::FDFSClient::upload_from_file("./makefile");
if (!id.has_value()) {
ERR("上传文件失败");
abort();
}
DBG("上传文件成功:{}", id.value());
// 2.从FastDFS服务器下载文件到本地
bool ret = xzyfdfs::FDFSClient::download_to_file(id.value(), "./makefile.download");
if (!ret) {
ERR("下载文件失败");
abort();
}
DBG("下载文件成功:{}", id.value());
return id.value();
}
// 基于内存的上传下载测试
std::string buff_test() {
// 1.从文件中读取数据到内存
std::string body;
xzyutil::FUTIL::read("./makefile", body);
// 2.从内存上传至FastDFS服务器
std::optional<std::string> id = xzyfdfs::FDFSClient::upload_from_buff(body);
if (!id.has_value()) {
ERR("上传内存数据失败");
abort();
}
DBG("上传内存数据成功:{}", id.value());
// 3.从FastDFS服务器下载文件到内存
std::string buff;
bool ret = xzyfdfs::FDFSClient::download_to_buff(id.value(), buff);
if (!ret) {
ERR("下载内存数据失败");
abort();
}
DBG("下载内存数据成功:{}", id.value());
// 4.将内存中的数据写入文件
xzyutil::FUTIL::write("./makefile.download2", buff);
return id.value();
}
// 多线程上传测试
void multi_thread_test(std::vector<std::string>& arr, std::mutex& mtx) {
// 1.创建3个线程
std::vector<std::thread> threads;
for (int i = 0; i < 3; i++) {
threads.emplace_back([&](){
// 2.每个线程上传3个文件
for (int i = 0; i < 3; i++) {
std::optional<std::string> id = xzyfdfs::FDFSClient::upload_from_file("./makefile");
if (!id.has_value()) {
ERR("上传文件失败");
abort();
}
// 3.将上传成功的文件ID添加到数组中
{
std::lock_guard<std::mutex> lock(mtx);
arr.push_back(id.value());
}
DBG("上传文件成功:{}", id.value());
}
});
}
// 4.等待所有线程执行完毕
for (auto& th : threads) {
th.join();
}
}
// 删除所有上传的文件测试
void remove_test(std::vector<std::string>& arr) {
for (auto& id : arr) {
bool ret = xzyfdfs::FDFSClient::remove(id);
if (!ret) {
ERR("文件删除失败:{}", id);
abort();
}
DBG("文件删除成功:{}", id);
}
}
int main()
{
// 1.初始化日志模块,配置并初始化FastDFS客户端
xzylog::log_init();
xzyfdfs::fdfs_settings settings = {
.tracker_servers = {"192.168.174.128:22122"}
};
xzyfdfs::FDFSClient::init(settings);
// 2.执行上传、下载、删除测试
std::string id = file_test();
std::string id2 = buff_test();
std::vector<std::string> arr = {id, id2};
remove_test(arr);
arr.clear();
// 3.执行多线程上传测试
std::mutex mtx;
multi_thread_test(arr, mtx);
// 4.删除所有上传的文件
remove_test(arr);
// 5.释放FastDFS客户端
xzyfdfs::FDFSClient::destroy();
return 0;
}

通过 md5sum 判断是否为同一个文件:

火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。
更多推荐
所有评论(0)