简介
FastDFS 是基于 C 语言开发的,是一个轻量级开源的高性能分布式文件系统。主要功能有:文件存储、文件同步、文件访问(文件上传/下载),解决了大容量的文件存储和高并发访问的问题,文件存取时实现了负载均衡。FastDFS 特别适合中大型网站以文件为载体的在线服务,适合存储 4KB ~ 500MB 之间的小文件,如照片共享网站、视频共享网站(图片、文档、音频、视频等等)。
术语
Client 客户端,实现文件上传下载的服务器,就是我们自己的项目所部署在的服务器。通过专有接口,使用 TCP/IP 协议与跟踪服务器或存储服务器进行数据交互。FastDFS 向使用者提供基本文件访问接口,比如 upload、download、append、delete等,以客户端库的方式提供给用户使用。
Tracker Server 跟踪服务器,负责文件访问的调度和负载均衡,负责管理所有的 Storage Server 和 group 组/卷。
Storage Server 存储服务器,负责文件存储,文件同步/备份,提供文件访问接口,文件元数据管理。以 group 为单位,每个 group 内可以有多台 Storage Server,数据互为备份,达到容灾的目的。每个 Storage 在启动以后会主动连接 Tracker,告知自己所属 group 等存储相关信息,并保持周期性心跳。
Group 组,也可称为 Volume 卷。同组内服务器上的文件是完全相的,同一组内的 Storage Server 之间是对等的,文件上传、删除等操作可以在任意一台 Storage Server 上进行。
Metadata 文件系统中存储的数据分为数据和元数据两部分,数据是指文件中的实际数据,即文件的实际内容;而元数据是用来描述一个文件特征的系统数据,诸如访问权限、文件拥有者以及文件数据块的分布信息等等。如果文件是一张图片,元数据就是图片的宽,高等等。
安装
下载资源
1、github: https://github.com/happyfish100
下载 libfastcommon , fastdfs ,fastdfs-nginx-module、libserverframe 三个项目对应的压缩包
2、通过资源地址:https://sourceforge.net/projects/fastdfs/files/ 下载
3、giteet: https://toscode.gitee.com/fastdfs100/projects
libfastcommon :从 fastdfs 项目和 fastdht 项目中提取出来的公共 C 函数库。
fastdfs :FastDFS 核心项目。
fastdfs-nginx-module :Nginx 整合 FastDFS 时 Nginx 需要添加的模块资源。
libserverframe :网络框架库 libserverframe,替换原有的 tracker nio 和 storage nio 两个模块
安装依赖
FastDFS 是基于 C 语言开发的,安装它之前必须先安装它所依赖的环境。
yum install -y make cmake gcc gcc-c++
安装zip、unzip
yum install zip
yum install unzip
libfastcommon-master.zip
#解压 libfastcommon-master 至当前所在目录
unzip libfastcommon-master.zip
# 进入解压后的 libfastcommon-master 目录
cd libfastcommon-master
# 编译并安装
./make.sh && ./make.sh install
fastdfs-master.zip
#解压 libserverframe-maste 至当前所在目录
unzip libserverframe-master.zip
# 进入解压后的 libfastcommon-master 目录
cd libserverframe-master
# 编译并安装
./make.sh && ./make.sh install
fastdfs 默认安装在以下位置:
/usr/bin :可执行文件
/etc/fdfs :配置文件
/etc/init.d :主程序代码 (如果改文件中没有 fdfs_storaged 和fdfs_trackerd 时,拷贝fastdfs-master中init.d总共的文件)
/usr/include/fastdfs :插件组
启动 Tracker
tracker 和 storage 其实都是 fastdfs ,只不过启动时通过不同的配置文件启动,所扮演的角色不同而已。也就是说,安装 tracker 和 storage 就是在安装 fastdfs ,然后通过每个角色具体的配置文件启动即可。
查看 /etc/fdfs 目录下所有配置文件。
client.conf http.conf mime.types storage.conf storage_ids.conf tracker.conf
client.conf.sample :客户端的配置文件,测试用 storage.conf.sample :存储器的配置文件 tracker.conf.sample :跟踪器的配置文件
编辑 tracker.conf 配置文件
#允许访问 tracker 服务器的 IP 地址,为空则表示不受限制
bind_addr =
#tracker 服务监听端口
port = 22122
#tracker 服务器的运行数据和日志的存储父路径(需要提前创建好)
base_path = /fastdfs/tracker
#tracker 服务器 HTTP 协议下暴露的端口
http.server_port = 8080
启动 tracker 服务
#创建 tracker 服务器的运行数据和日志的存储父路径
mkdir -p /fastdfs/tracker
#启动 tracker 服务
service fdfs_trackerd start
#查看 tracker 服务状态
service fdfs_trackerd status
#重启 tracker 服务
service fdfs_trackerd restart
#停止 tracker 服务
service fdfs_trackerd stop
启动 Storage
编辑 storage.conf 配置文件
#storage 组名/卷名,默认为 group1
group_name = group1
#允许访问 storage 服务器的 IP 地址,为空则表示不受限制
bind_addr =
#storage 服务器的运行数据和日志的存储父路径(需要提前创建好)
base_path = /fastdfs/storage/base
#storage 服务器中客户端上传的文件的存储父路径(需要提前创建好)
store_path0 = /fastdfs/storage/store
#storage 服务器 HTTP 协议下暴露的端口
http.server_port = 8888
#tracker 服务器的 IP 和端口
tracker_server = 192.168.10.101:22122
启动 storage 服务
#创建 storage 服务器的运行数据和日志的存储父路径
mkdir -p /fastdfs/storage/base
#创建 storage 服务器中客户端上传的文件的存储父路径
mkdir -p /fastdfs/storage/store
#启动 storage 服务
service fdfs_storaged start
#查看 storage 服务状态
service fdfs_storaged status
#重启 storage 服务
service fdfs_storaged restart
#停止 storage 服务
service fdfs_storaged stop
Client 操作
FastDFS 向使用者提供基本文件访问接口,比如 upload、download、append、delete 等,以客 户端库的方式提供给用户使用。
编辑 client.conf 配置文件
#client 客户端的运行数据和日志的存储父路径(需要提前创建好)
base_path = /fastdfs/client
#tracker 服务器的 IP(配置虚拟ip eth0) 和端口
tracker_server = 192.168.10.101:22122
记得 mkdir -p /fastdfs/client 创建 Client 目录
注:
开放22122和23000端口
上传
1、进入/usr/bin目录
2、执行上传命令
./fdfs_upload_file /etc/fdfs/client.conf /usr/local/tool/FastDFS/libfastcommon-master.zip
3、返回
group1/M00/00/00/MekNfmN9yjSAez-uAAVXdOvFp2k246.zip
注:
group1:卷名
M00:虚拟磁盘路径。与 Storage 配置文件中磁盘选项 store_path* 对应。如果配置了store_path0 则是M00 ,如果配置了 store_path1 则是 M01 ,以此类推。比如:store_path0 =
00/00:文件路径
下载
/usr/bin/fdfs_download_file /etc/fdfs/client.conf group1/M00/00/00/MekNfmN9yjSAez-uAAVXdOvFp2k246.zip
删除
/usr/bin/fdfs_delete_file /etc/fdfs/client.conf group1/M00/00/00/MekNfmN9yjSAez-uAAVXdOvFp2k246.zip
Nginx 整合 FastDFS 时 Nginx 需要添加 fastdfs-nginx-module 模块
fastdfs-nginx-module-master.zip
cp src/mod_fastdfs.conf /etc/fdfs/
vim /etc/fdfs/mod_fastdfs.conf 编辑配置文件,主要关注以下部分
#tracker 服务器的 IP 和端口
tracker_server = 192.168.10.101:22122
#url 地址是否包含组名/卷名
url_have_group_name = true
#数据组/卷对应的路径地址
store_path0 = /fastdfs/storage/store
复制 fastdfs 安装包中的两个配置文件 http.conf 和 mime.types 到 /etc/fdfs 目录中
cp fastdfs-master/conf/http.conf /etc/fdfs/
cp fastdfs-master/conf/mime.types /etc/fdfs/
配置 Nignx
指定安装模块
--add-module=/usr/local/src/fastdfs-nginx-module-master/src
nginx.conf 编辑配置文件
location ~/group[0-9]/{
ngx_fastdfs_module;
}
项目使用
下载项目fastdfs-client-java并打成jar
pom
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.29-SNAPSHOT</version>
</dependency>
配置
添加配置文件fdfs_client.conf,增加以下配置
connect_timeout = 2
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 80
http.anti_steal_token = no
http.secret_key = FastDFS1234567890
tracker_server = 192.168.56.10:22122
http.anti_steal_token:配置防盗链,no表示不开启(详细参考FastDFS防盗链一节) http.secret_key:防盗链密码 tracker_server:tracker连接信息,有多少tracker就配置几个
工具类
public class FastDfsUtil {
private static Logger logger = LoggerFactory.getLogger(FastDfsUtil.class);
// 获取配置文件地址
private static final String CONF_FILENAME = Thread.currentThread() .getContextClassLoader().getResource("").getPath() + "fdfs_client.conf";
// Storage 存储服务器客户端
private static StorageClient storageClient = null;
static {
try {
// 加载配置文件
ClientGlobal.init(CONF_FILENAME);
// 初始化 Tracker 客户端
TrackerClient trackerClient = new TrackerClient(ClientGlobal.g_tracker_group);
// 初始化 Tracker 服务端
TrackerServer trackerServer = trackerClient.getTrackerServer();
// 初始化 Storage 服务端
StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
// 初始化 Storage 客户端
storageClient = new StorageClient(trackerServer, storageServer);
}catch (Exception e) {
logger.error("FastDfs初始化异常",e);
}
}
/**
* 文件上传
* @param multipartFile
* @return
*/
public static String[] uploadFile(MultipartFile multipartFile){
InputStream inputStream=null;
try {
if(!multipartFile.isEmpty()){
//获取流
inputStream = multipartFile.getInputStream();
String fileName=multipartFile.getOriginalFilename();
// 查看文件的长度
int len = inputStream.available();
// 初始化元数据数组
NameValuePair[] metaList = new NameValuePair[2];
// 第一组元数据,文件的原始名称
metaList[0] = new NameValuePair("file_name", fileName);
// 第二组元数据,文件的长度
metaList[1] = new NameValuePair("file_length",String.valueOf(len));
// 创建对应长度的字节数组
byte[] fileBuff = fileBuff = new byte[len];
// 将输入流中的字节内容,读到字节数组中
inputStream.read(fileBuff);
return storageClient.upload_file(fileBuff,getFileExt(fileName),metaList);
}
}catch (Exception e){
logger.error("FastDfs上传异常",e);
}finally {
if(inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
logger.error("FastDfs上传关闭流异常",e);
}
}
}
return null;
}
/**
* 获取文件后缀名(不带点)
*
* @param fileName
* @return 如:"jpg" or ""
*/
private static String getFileExt(String fileName) {
if (StringUtils.isBlank(fileName) || !fileName.contains(".")) {
return "";
}
return fileName.substring(fileName.lastIndexOf(".") + 1); // 不带最后的点
}
/**
* 获取文件详情
*
* @param groupName 组/卷名,默认值:group1
* @param remoteFileName 文件名,例
如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
* @return 文件详情
*/
public static FileInfo getFileInfo(String groupName, String remoteFileName){
try {
return storageClient.get_file_info(groupName == null ? "group1" :groupName, remoteFileName);
} catch (Exception e) {
logger.error("FastDfs获取文件详情异常",e);
}
return null;
}
/**
* 获取元数据
*
* @param groupName 组/卷名,默认值:group1
* @param remoteFileName 文件名,例
如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
* @return 文件的元数据数组
*/
public static NameValuePair[] getMetaData(String groupName, String remoteFileName) {
try {
// 根据组名和文件名通过 Storage 客户端获取文件的元数据数组
return storageClient.get_metadata(groupName == null ? "group1" :groupName, remoteFileName);
} catch (Exception e) {
logger.error("FastDfs获取文件的元数据异常",e);
}
return null;
}
/**
* 文件下载
*
* @param groupName 组/卷名,默认值:group1
* @param remoteFileName 文件名,例
如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
* @return 文件的字节输入流
*/
public static InputStream downloadFile(String groupName, String remoteFileName) {
try {
// 根据组名和文件名通过 Storage 客户端获取文件的字节数组
byte[] bytes = storageClient.download_file(groupName == null ? "group1" : groupName, remoteFileName);
// 返回字节流对象
InputStream inputStream = new ByteArrayInputStream(bytes);
return inputStream;
} catch (Exception e) {
logger.error("FastDfs下载异常",e);
}
return null;
}
/**
* 文件删除
*
* @param groupName 组/卷名,默认值:group1
* @param remoteFileName 文件名,例
如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
* @return 0为成功,非0为失败
*/
public static int deleteFile(String groupName, String remoteFileName) {
int result = -1;
try {
// 根据组名和文件名通过 Storage 客户端删除文件
result = storageClient.delete_file(groupName == null ? "group1" :groupName, remoteFileName);
} catch (Exception e) {
logger.error("FastDfs删除异常",e);
}
return result;
}
}