FastDFS 是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。
FastDFS 为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用 FastDFS 很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
FastDFS 服务端有两个角色:跟踪器(tracker)和存储节点(storage)。跟踪器主要做调度工作,在访问上起负载均衡的作用。
存储节点存储文件,完成文件管理的所有功能:就是这样的存储、同步和提供存取接口,FastDFS 同时对文件的 metadata进行管理。所谓文件的metadata 就是文件的相关属性,以键值对(key value)方式表示,如:width=1024,其中的 key 为 width,value 为 1024。文件metadata是文件属性列表,可以包含多个键值对。
跟踪器和存储节点都可以由一台或多台服务器构成。跟踪器和存储节点中的服务器均可以随时增加或下线而不会影响线上服务。其中跟踪器中的所有服务器都是对等的,可以根据服务器的压力情况随时增加或减少。
为了支持大容量,存储节点(服务器)采用了分卷(或分组)的组织方式。存储系统由一个或多个卷组成,卷与卷之间的文件是相互独立的,所有卷的文件容量累加就是整个存储系统中的文件容量。一个卷可以由一台或多台存储服务器组成,一个卷下的存储服务器中的文件都是相同的,卷中的多台存储服务器起到了冗余备份和负载均衡的作用。
在卷中增加服务器时,同步已有的文件由系统自动完成,同步完成后,系统自动将新增服务器切换到线上提供服务。当存储空间不足或即将耗尽时,可以动态添加卷。只需要增加一台或多台服务器,并将它们配置为一个新的卷,这样就扩大了存储系统的容量。
FastDFS 中的文件标识分为两个部分:卷名和文件名,二者缺一不可。
架构图
上传流程
client询问tracker上传到的 storage,不需要附加参数;
tracker返回一台可用的storage;
client直接和storage 通讯完成文件上传
下载流程
client询问tracker下载文件的storage,参数为文件标识(组名和文件名);
tracker返回一台可用的storage;
client直接和storage通讯完成文件下载。
术语介绍
TrackerServer:跟踪服务器,主要做调度工作,在访问上起负载均衡的作用。记录storage server 的状态,是连接 Client 和 Storage server 的枢纽。
Storage Server:存储服务器,文件和meta data 都保存到存储服务器上
group:组,也称为卷。同组内服务器上的文件是完全相同的
文件标识:包括两部分:组名和文件名(包含路径)
meta data:文件相关属性,键值对(Key Value Pair)方式,如:width=1024,heigth=768
同步机制
同一组内的 storage server 之间是对等的,文件上传、删除等操作可以在任意一台 storage server 上进行;
文件同步只在同组内的 storage server 之间进行,采用 push 方式,即源服务器同步给目标服务器;
源头数据才需要同步,备份数据不需要再次同步,否则就构成环路了;
上述第二条规则有个例外,就是新增加一台storage server时,由已有的一台storage server将已有的所有数据(包括源头数据和备份数据)同步给该新增服务器
FastDFS 运行时目录结构
Tracker Server 目录
${base_path}
|__data
| |__storage_groups.dat:存储分组信息
| |__storage_servers.dat:存储服务器列表
|__logs
|__trackerd.log:tracker server 日志文件
Storage Server 目录
${base_path}
|__data
| |__.data_init_flag:当前 storage server 初始化信息
| |__storage_stat.dat:当前 storage server 统计信息
| |__sync:存放数据同步相关文件
| | |__binlog.index:当前的 binlog 文件索引号
| | |__binlog.###:存放更新操作记录(日志)
| | |__${ip_addr}_${port}.mark:存放同步的完成情况
| |
| |__一级目录:256 个存放数据文件的目录,如:00, 1F
| |__二级目录:256 个存放数据文件的目录
|__logs
|__storaged.log:storage server 日志文件
FastDFS 和其他文件存储的简单对比
FastDFS 和集中存储方式对比
指标FastDFSNFS集中存储设备如 NetApp、NAS
线性扩容性高差差
文件高并发访问性能高差一般
文件访问方式专用APIPOSIXPOSIX
硬件成本较低中等高
相同内容文件只保存一份支持不支持不支持
FastDFS 和 和 mogileFS 对比
指标FastDFSmogileFS
系统简洁性简洁 只有两个角色:tracker和storage一般 有三个角色:tracker、storage和存储文件信息的mysql db
系统性能很高(没有使用数据库,文件同步直接点对点,不经过tracker中转)高(使用mysql来存储文件索引信息,文件同步通过tracker调度和中转)
系统稳定性高(C语言开发,可以支持高并发和高负载)一般(Perl语言开发,高并发和高负载支持一般)
RAID方式分组(组内冗余),灵活性较大动态冗余,灵活性一般
通信协议专用协议,下载文件支持HTTPHTTP
技术文档较详细较少
文件附加属性(meta data)支持不支持
相同内容文件只保存一份支持不支持
下载文件时支持文件偏移量支持不支持
安装
安装FastDFS
安装FastDFS依赖
FastDFS是C语言开发的应用。安装必须使用make、cmake和gcc编译器。
yum install-ymakecmakegccgcc-c++
解压FastDFS核心库
libfastcommon是从FastDFS和FastDHT中提取出来的公共C函数库
unzip libfastcommon-1.0.43.zip-d/usr/local/fastdfs
编译安装
libfastcommon没有提供make命令安装文件。使用的是shell脚本执行编译和安装。shell脚本为 make.sh
编译
./make.sh
安装
./make.sh install
有固定的默认安装位置。在/usr/lib64 和/usr/include/fastcommon 两个目录中。
cd/usr/local/fastdfs/libfastcommon-1.0.43
./make.sh
./make.sh install
创建软连接
因为FastDFS主程序设置的lib目录是/usr/local/lib,所以需要创建软链接
ln-s/usr/lib64/libfastcommon.so /usr/local/lib/libfastcommon.so
ln-s/usr/local/lib64/libfdfsclient.so /usr/local/lib/libfdfsclient.so
ln-s/usr/local/lib64/libfdfsclient.so /usr/lib/libfdfsclient.so
解压FastDFS并安装
解压
tar -zxf fastdfs-6.06.tar.gz -C /usr/local/fastdfs
修改安装路径(可选)
vi /usr/local/fastdfs/FastDFS/make.sh
TARGET_PREFIX=$DESTDIR/usr -> TARGET_PREFIX=$DESTDIR/usr/local
编译安装
./make.sh
./make.sh install
安装后,FastDFS主程序所在的位置是:
/usr/local/bin可执行文件所在位置。默认安装在/usr/bin 中。
/etc/fdfs配置文件所在位置。就是默认位置。
/usr/local/lib64主程序代码所在位置。默认在/usr/bin 中。
/usr/local/include/fastdfs包含的一些插件组所在位置。默认在/usr/include/fastdfs 中。
FastDFS安装后资源简介
服务脚本
/etc/init.d/目录中,脚本文件是 fdfs-storaged 和 fdfs-trackerd
配置文件模板
/etc/fdfs/目录中,配置文件是client.conf.sample 、storage.conf.sample 和tracker.conf.sample
tracker.conf.sample跟踪器服务配置文件模板
storage.conf.sample存储服务器配置文件模板
client.conf.sampleFastDFS 提供的命令行客户端配置文件模板。可以通过命令行测试FastDFS有效性。
内置命令
/usr/local/bin/目录中。命令有若干。可通过命令在控制台访问 FastDFS。
tracker基础配置
创建跟踪服务配置文件
FastDFS 提供了配置文件模板,可以根据模板创建需要使用的配置文件。
cd /etc/fdfs
cp tracker.conf.sample tracker.conf
修改配置文件
tracker.conf 配置文件用于描述跟踪服务的行为,需要进行下述修改:
vim /etc/fdfs/tracker.conf
port=22122 # 默认服务端口
base_path=/home/yuqing/fastdfs -> base_path=/fastdfs/tracker(自定义目录)
base_path是FastDFSTracker启动后使用的根目录。也就是data和logs所在位置。
创建自定义目录
为配置文件中定义的 base_path 变量创建对应的目录。
mkdir -p /fastdfs/tracker
启动服务
vim /etc/init.d/fdfs_trackerd
#将 PRG=/usr/bin/fdfs_trackerd 修改为 PRG=/usr/local/bin/fdfs_trackerd
/etc/init.d/fdfs_trackerd start
启动成功后,配置文件中base_path指向的目录中出现 FastDFS服务相关数据目录( data目录、logs 目录)
查看服务状态
/etc/init.d/fdfs_trackerd status
ps aux | grep fdfs
停止服务
/etc/init.d/fdfs_trackerd stop
重启服务
/etc/init.d/fdfs_trackerd restart
设置开机自启
vi /etc/rc.d/rc.local
新增内容 - /etc/init.d/fdfs_trackerd start
storage基础配置
创建存储服务配置文件
FastDFS提供了配置文件模板,可以根据模板创建需要使用的配置文件。
cd /etc/fdfs
cp storage.conf.sample storage.conf
修改配置文件
storage.conf 配置文件用于描述存储服务的行为,需要进行下述修改:
vim /etc/fdfs/storage.conf
base_path=/home/yuqing/fastdfs -> base_path=/fastdfs/storage/base (自定义目录) store_path0=/home/yuqing/fastdfs -> store_path0=/fastdfs/storage/store (自定义目录 ) tracker_server=192.168.2.109:22122 -> tracker_server=tracker服务IP:22122
base_path基础路径。用于保存 storage server 基础数据内容和日志内容的目录。
store_path0存储路径。是用于保存 FastDFS 中存储文件的目录,就是要创建 256*256个子目录的位置。base_path 和 store_path0 可以使用同一个目录。
tracker_server跟踪服务器位置。就是跟踪服务器的 ip 和端口。
创建自定义迷路
mkdir -p /fastdfs/storage/base
mkdir -p /fastdfs/storage/store
启动服务
要求tracker服务必须已启动
vim /etc/init.d/fdfs_storaged
将 PRG=/usr/bin/fdfs_storaged 修改为 PRG=/usr/local/bin/fdfs_storaged
/etc/init.d/fdfs_storaged start
启动成功后,配置文件中 base_path 指向的目录中出现 FastDFS 服务相关数据目录(data目录、logs 目录),配置文件中的store_path0指向的目录中同样出现 FastDFS 存储相关数据目录(data 目录)。其中$store_path0/data/目录中默认创建若干子孙目录(两级目录层级总计256*256 个目录),是用于存储具体文件数据的。
Storage 服务器启动比较慢,因为第一次启动的时候,需要创建 256*256 个目录。
查看服务状态
/etc/init.d/fdfs_storaged status
ps aux | grep fdfs
停止服务
/etc/init.d/fdfs_storaged stop
重启服务
/etc/init.d/fdfs_storaged restart
设置开机自启
vi /etc/rc.d/rc.local
新增内容 - /etc/init.d/fdfs_storaged start
因启动前提为 tracker 服务必须已启动,不推荐开启自启。
客户端基础配置
不是必须的。就是用于使用命令行测试 FastDFS 才需要配置的。
创建客户端配置文件
在tracker服务结点所在服务器中配置客户端。同样通过配置文件模板创建对应配置文件。
cd /etc/fdfs
cp client.conf.sample client.conf
修改配置文件
client.conf 配置文件中主要描述客户端的行为,需要进行下述修改:
vim /etc/fdfs/client.conf
base_path=/home/yuqing/fastdfs -> base_path=/fastdfs/client (自定义目录) tracker_server=192.168.2.109:22122 -> tracker_server=tracker 服务 IP:22122
base_path就是客户端命令行执行过程时临时数据存储位置。
创建自定义目录
mkdir -p /fastdfs/client
控制台测试FastDFS
命令所在:/usr/local/bin目录。(如果在安装FastDFS过程中,没有修改make.sh文件中的 TARGET_PREFIX 属性值,命令所在为/usr/bin目录)
上传文件
/usr/local/bin/fdfs_upload_file /etc/fdfs/client.conf /要上传的文件
上传结束后,返回 group1/M00/00/00/xxxxxxxxxx.xxx,检查 storage 服务结点中的$store_path0/data/00/00/目录中是否有上传的文件(一般情况上传的文件按顺序保存在$store_path0/data/00/00/目录中,不能完全保证 )。
测试的上传文件结果:group1/M00/00/00/wKgKZF4dXl6AHrE5ACBzEWqC2Sc349.jpg
卷名:group1
文件名:M00/00/00/wKgKZF4dXl6AHrE5ACBzEWqC2Sc349.jpg
其中M00是一个虚拟目录,相当于 windows 中的快捷方式,引用的是$store_path0/data目录。
删除文件
/usr/local/bin/fdfs_delete_file /etc/fdfs/client.conf group1/M00/00/00/xxxxxxx.xxx
删除结束后,检查$store_path0/data/00/00/目录中是否还有文件。
安装Nginx组件
如果FastDFS中保存的是图片信息。希望在 WEB 应用中可以直接访问FastDFS中的图片进行显示。如果操作?
安装 Nginx是为了WEB应用中可以使用 HTTP协议直接访问Storage 服务中存储的文件。在 storage 结点所在服务器安装 Nginx 组件。
需要安装两部分内容。
Nginx 应用
在安装 nginx 应用的时候,同时要在 nginx 中增加一个 FastDFS 的组件Module。
解压fastdfs-nginx-module-1.22.tar.gz
tar -zxf fastdfs-nginx-module-1.22.tar.gz -C /usr/local/fastdfs
修改fastdfs-nginx-module-1.22.tar.gz源文件中的配置
此操作必须修改,否则Nginx编译会报错。
cd /usr/local/fastdfs/fastdfs-nginx-module-1.22/src
vim /usr/local/fastdfs/fastdfs-nginx-module-1.22/src/config
参数是用于配置安装nginx中的FastDFS组件的时候,在什么位置查找FastDFS核心代码。
源数据:
ngx_addon_name=ngx_http_fastdfs_module
if test -n "${ngx_module_link}"; then
ngx_module_type=HTTP
ngx_module_name=$ngx_addon_name
ngx_module_incs="/usr/local/include"
ngx_module_libs="-lfastcommon -lfdfsclient"
ngx_module_srcs="$ngx_addon_dir/ngx_http_fastdfs_module.c"
ngx_module_deps=
CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64 -DFDFS_OUTPUT_CHUNK_SIZE='256*1024' -DFDFS_MOD_CONF_FILENAME='\"/etc/fdfs/mod_fastdfs.conf\"'"
. auto/module
else
HTTP_MODULES="$HTTP_MODULES ngx_http_fastdfs_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_fastdfs_module.c"
CORE_INCS="$CORE_INCS /usr/local/include"
CORE_LIBS="$CORE_LIBS -lfastcommon -lfdfsclient"
CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64 -DFDFS_OUTPUT_CHUNK_SIZE='256*1024' -DFDFS_MOD_CONF_FILENAME='\"/etc/fdfs/mod_fastdfs.conf\"'"
fi
修改后内容:(如果安装FastDFS时,没有修改make.sh文件,则改为:CORE_INCS="$CORE_INCS /usr/include/fastdfs /usr/include/fastcommon/")
ngx_addon_name=ngx_http_fastdfs_module
if test -n "${ngx_module_link}"; then
ngx_module_type=HTTP
ngx_module_name=$ngx_addon_name
ngx_module_incs="/usr/local/include/fastdfs /usr/include/fastcommon/"
ngx_module_libs="-lfastcommon -lfdfsclient"
ngx_module_srcs="$ngx_addon_dir/ngx_http_fastdfs_module.c"
ngx_module_deps=
CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64 -DFDFS_OUTPUT_CHUNK_SIZE='256*1024' -DFDFS_MOD_CONF_FILENAME='\"/etc/fdfs/mod_fastdfs.conf\"'"
. auto/module
else
HTTP_MODULES="$HTTP_MODULES ngx_http_fastdfs_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_fastdfs_module.c"
CORE_INCS="$CORE_INCS /usr/local/include/fastdfs /usr/include/fastcommon/"
CORE_LIBS="$CORE_LIBS -lfastcommon -lfdfsclient"
CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64 -DFDFS_OUTPUT_CHUNK_SIZE='256*1024' -DFDFS_MOD_CONF_FILENAME='\"/etc/fdfs/mod_fastdfs.conf\"'"
fi
安装Nginx
安装依赖
yum install -y gcc gcc-c++ make automake autoconf libtool pcre pcre-develzlib zlib-devel openssl openssl-devel
解压Ngxin
tar -zxf nginx-1.16.1.tar.gz -C /usr/local/fastdfs/
配置Nginx安装信息
cd /usr/local/fastdfs/nginx-1.16.1/
mkdir -p /var/temp/nginx
./configure \
--prefix=/usr/local/nginx \
--pid-path=/var/run/nginx/nginx.pid \
--lock-path=/var/lock/nginx.lock \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--with-http_gzip_static_module \
--http-client-body-temp-path=/var/temp/nginx/client \
--http-proxy-temp-path=/var/temp/nginx/proxy \
--http-fastcgi-temp-path=/var/temp/nginx/fastcgi \
--http-uwsgi-temp-path=/var/temp/nginx/uwsgi \
--http-scgi-temp-path=/var/temp/nginx/scgi \
--add-module=/usr/local/fastdfs/fastdfs-nginx-module-1.22/src
--add-module 必须定义,此配置信息是用于指定安装 Nginx时需要加载的模块,如果未指定, Nginx安装过程不会加载fastdfs-nginx-module 模块,后续功能无法实现。
安装
make
make install
配置 fastdfs-nginx-module 模块配置文件
复制配置文件/usr/local/fastdfs/fastdfs-nginx-module-1.22/src/mod_fastdfs.conf到/etc/fdfs目录中
cp /usr/local/fastdfs/fastdfs-nginx-module-1.22/src/mod_fastdfs.conf /etc/fdfs/
cd /etc/fdfs/
#修改配置文件 mod_fastdfs.conf
vim mod_fastdfs.conf
源配置:
connect_timeout=2 #连接超时时间,单位秒
tracker_server=tracker:22122 #tracker 服务结点
url_have_group_name = false #URL中是否包含group名称
store_path0=/home/yuqing/fastdfs # storage 服务结点的存储位置,与配置storage结点一致
参考修改值:
connect_timeout=10
tracker_server=192.168.2.109:22122
url_have_group_name = true
store_path0=/fastdfs/storage/store
提供 FastDFS需要的HTTP配置文件
复制 FastDFS安装包中的两个配置文件(http.conf和 mime.types)到/etc/fdfs 目录中
cp /usr/local/fastdfs/fastdfs-6.06/conf/http.conf /etc/fdfs/
cp /usr/local/fastdfs/fastdfs-6.06/conf/mime.types /etc/fdfs/
创建nginx启动需要的软连接
创建软连接
ln -s /usr/local/lib64/libfdfsclient.so /usr/lib64/libfdfsclient.so
nginx启动后,会在默认的/usr/lib64目录中查找需要的so文件。如果在安装FastDFS时,修改了make.sh文件中的 TARGET_PREFIX参数,则必须创建此软连接
创建网络访问存储服务的软连接
ln -s /fastdfs/storage/store/data/ /fastdfs/storage/store/data/M00
在上传文件到 FastDFS 后,FastDFS 会返回 group1/M00/00/00/xxxxxxxxxx.xxx。其中 group1是卷名,在mod_fastdfs.conf配置文件中已配置了url_have_group_name,以保证URL解析正确。而其中的 M00是FastDFS保存数据时使用的虚拟目录,需要将这个虚拟目录定位到真实数据目录上。
修改 nginx 配置文件
cd /usr/local/nginx/conf
vim nginx.conf
参考修改配置:( 部分配置信息,不要完整复制 )
user root; # Nginx 需要访问 linux文件系统,必须有文件系统的权限。User root代表nginx 访问文件系统的权限是root用户权限。如果不开启权限,可能有404访问错误。
server{
listen 8888; # storage 配置中,有 http.server_port=8888 的配置信息,必须一致。配置文件是/etc/fdfs/storaged.conf
server_name localhost;
location ~/group([0-9])/M00{
ngx_fastdfs_module;
}
}
重启storage,启动nginx
/etc/init.d/fdfs_storaged restart
/usr/local/nginx/sbin/nginx
测试WEB访问存储服务中的文件
使用浏览器查看 FastDFS 中保存的文件:
http://ip:8888/group1/M00/00/00/xxxxxxx.xxx
测试上传的文件:group1/M00/00/00/wKgKZF4dasWAFxh7ACBzEWqC2Sc193.jpg
测试 WEB 访问地址:
http://192.168.10.100:8888/group1/M00/00/00/wKgKZF4dasWAFxh7ACBzEWqC2Sc193.jpg
Java API
依赖
<!--FastDFS依赖-->
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.29-SNAPSHOT</version>
</dependency>
常用类
CLientGlobal
用于加载配置文件的公共客户端工具。
常用方法:
init(String conf_filename);根据配置文件路径及命名,加载配置文件并设置客户端公共参数,配置文件类型为 conf 文件。可以使用绝对路径或相对路径加载。
initByProperties(Properties props);根据 Properties 对象设置客户端公共参数。
注意:使用 conf 或 properties 进行客户端参数配置时,参数 key 命名不同。
TrackerClient
跟踪器客户端类型。创建此类型对象时,需传递跟踪器组,就是跟踪器的访问地址信息。无参构造方法默认使用 ClientGlobal.g_tracker_group 常量作为跟踪器组来构造对象。
创建对象的方式为:
new TrackerClient();或new TrackerClient(ClientGlobal.g_tracker_group)
TrackerServer
跟踪器服务类型。此类型的对象是通过跟踪器客户端对象构建的。实质上就是一个与FastDFS Tracker Server 的链接对象。是代码中与 Tracker Server 链接的工具。
构建对象的方式为:
trackerClient. getTrackerServer ();
StorageServer
存储服务类型。此类型的对象是通过跟踪器客户端对象构建的。实质上就是一个与FastDFS Storage Server 的链接对象。是代码中与 StroageServer 链接的工具。获取的具体存储服务链接,是由 Tracker Server 分配的,所以构建存储服务对象时,需要依赖跟踪器服务对象。
构建对象的方式为:
trackerClient.getStoreStorage(trackerServer);
StorageClient
存储客户端类型。此类型的对象是通过构造方法创建的。创建时,需传递跟踪服务对象和存储服务对象。此对象实质上就是一个访问 FastDFS Storage Server 的客户端对象,用于实现文件的读写操作。
创建对象的方式为:
new StorageClient(trackerServer, storageServer);
常用方法有:
upload_file(String local_filename, String file_ext_name, NameValuePair[] meta_list);上传文件的方法,参数 local_filename 为要上传的本地文件路径及文件名,可使用绝对路径或相对路径;参数file_ext_name为上传文件的扩展名,如果传递null,则自动解析文件扩展名;参数 meta_list 是用于设置上传文件的源数据的,如上传用户、上传描述等。
download_file(String group_name, String remote_file_name);下载文件的方法,参数group为组名/卷名,就是 Storage Server中/etc/fdfs/storage.conf 配置文件中配置的group_name 参数值,也是要下载的文件所在组/卷的命名;参数 remote_file_name 为要下载的文件的路径及文件名。
delete_file(String group_name, String remote_file_name);删除文件的方法,参数含义同download_file方法参数。
Demo
创建项目
添加依赖
<!--FastDFS依赖-->
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.29-SNAPSHOT</version>
</dependency>
<!--thymeleaf 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
在resources目录下添加配置文件
fdfs_client.conf
注1:tracker_server指向您自己IP地址和端口,1-n个
注2:除了tracker_server,其它配置项都是可选的
#连接超时
connect_timeout = 2
#网络超时
network_timeout = 30
#编码格式
charset = UTF-8
#tracker端口号
http.tracker_http_port = 8080
#防盗链功能
http.anti_steal_token = no
#秘钥
http.secret_key = FastDFS1234567890
#tracker ip:端口号
tracker_server = 192.168.10.100:22122
#连接池配置
connection_pool.enabled = true
connection_pool.max_count_per_entry = 500
connection_pool.max_idle_time = 3600
connection_pool.max_wait_time_in_ms = 1000
添加FastDFS文件对象
FastDFSFile.java
package com.xxxx.fastdfsdemo;
import java.util.Arrays;
/**
* 文件上传对象类
* @author zhoubin
* @since 1.0.0
*/
public class FastDFSFile {
private String name;
private byte[] content;
private String ext;
private String md5;
private String author;
private String height;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
}
public String getExt() {
return ext;
}
public void setExt(String ext) {
this.ext = ext;
}
public String getMd5() {
return md5;
}
public void setMd5(String md5) {
this.md5 = md5;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
public FastDFSFile() {
}
public FastDFSFile(String name, byte[] content, String ext) {
this.name = name;
this.content = content;
this.ext = ext;
}
@Override
public String toString() {
return "FastDFSFile{" +
"name='" + name + '\'' +
", content=" + Arrays.toString(content) +
", ext='" + ext + '\'' +
", md5='" + md5 + '\'' +
", author='" + author + '\'' +
", height='" + height + '\'' +
'}';
}
}
编写FastDFS工具类
FastDFSClient.java
package com.xxxx.fastdfsdemo;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.FileInfo;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 文件上传工具类
*
* @author zhoubin
* @since 1.0.0
*/
public class FastDFSClient {
private static Logger logger = LoggerFactory.getLogger(FastDFSClient.class);
//ClientGlobal.init 方法会读取配置文件,并初始化对应的属性。
static{
try {
String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
ClientGlobal.init(filePath);
} catch (Exception e) {
logger.error("FastDFS Client Init Fail!",e);
}
}
/**
* 上传文件
* @param file
* @return
*/
public static String[] upload(FastDFSFile file) {
logger.info("File Name: " + file.getName() + "File Length:" + file.getContent().length);
//文件属性信息
NameValuePair[] meta_list = new NameValuePair[1];
meta_list[0] = new NameValuePair("author", file.getAuthor());
long startTime = System.currentTimeMillis();
String[] uploadResults = null;
StorageClient storageClient=null;
try {
//获取storage客户端
storageClient = getStorageClient();
//上传
uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
} catch (IOException e) {
logger.error("IO Exception when uploadind the file:" + file.getName(), e);
} catch (Exception e) {
logger.error("Non IO Exception when uploadind the file:" + file.getName(), e);
}
logger.info("upload_file time used:" + (System.currentTimeMillis() - startTime) + " ms");
//验证上传结果
if (uploadResults == null && storageClient!=null) {
logger.error("upload file fail, error code:" + storageClient.getErrorCode());
}
//上传文件成功会返回 groupName。
logger.info("upload file successfully!!!" + "group_name:" + uploadResults[0] + ", remoteFileName:" + " " + uploadResults[1]);
return uploadResults;
}
/**
* 获取文件信息
* @param groupName
* @param remoteFileName
* @return
*/
public static FileInfo getFile(String groupName, String remoteFileName) {
try {
StorageClient storageClient = getStorageClient();
return storageClient.get_file_info(groupName, remoteFileName);
} catch (IOException e) {
logger.error("IO Exception: Get File from Fast DFS failed", e);
} catch (Exception e) {
logger.error("Non IO Exception: Get File from Fast DFS failed", e);
}
return null;
}
/**
* 下载文件
* @param groupName
* @param remoteFileName
* @return
*/
public static InputStream downFile(String groupName, String remoteFileName) {
try {
StorageClient storageClient = getStorageClient();
byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
InputStream ins = new ByteArrayInputStream(fileByte);
return ins;
} catch (IOException e) {
logger.error("IO Exception: Get File from Fast DFS failed", e);
} catch (Exception e) {
logger.error("Non IO Exception: Get File from Fast DFS failed", e);
}
return null;
}
/**
* 删除文件
* @param groupName
* @param remoteFileName
* @throws Exception
*/
public static void deleteFile(String groupName, String remoteFileName)
throws Exception {
StorageClient storageClient = getStorageClient();
int i = storageClient.delete_file(groupName, remoteFileName);
logger.info("delete file successfully!!!" + i);
}
/**
* 生成Storage客户端
* @return
* @throws IOException
*/
private static StorageClient getStorageClient() throws IOException {
TrackerServer trackerServer = getTrackerServer();
StorageClient storageClient = new StorageClient(trackerServer, null);
return storageClient;
}
/**
* 生成Tracker服务器端
* @return
* @throws IOException
*/
private static TrackerServer getTrackerServer() throws IOException {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getTrackerServer();
return trackerServer;
}
/**
* 获取文件路径
* @return
* @throws IOException
*/
public static String getTrackerUrl() throws Exception {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getTrackerServer();
StorageServer storeStorage = trackerClient.getStoreStorage(trackerServer);
return "http://"+storeStorage.getInetSocketAddress().getHostString()+":8888/";
}
}
编写Controller
package com.xxxx.fastdfsdemo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import java.io.IOException;
import java.io.InputStream;
/**
* 文件上传Controller
* @author zhoubin
*/
@Controller
public class UploadController {
private static Logger logger = LoggerFactory.getLogger(UploadController.class);
/**
* 页面跳转
* @return
*/
@GetMapping("/")
public String index() {
return "upload";
}
/**
* 上传文件
* @param file
* @param redirectAttributes
* @return
*/
@PostMapping("/upload")
public String singleFileUpload(@RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {
if (file.isEmpty()) {
redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
return "redirect:uploadStatus";
}
try {
// 上传文件拿到返回的文件路径
String path=saveFile(file);
redirectAttributes.addFlashAttribute("message",
"You successfully uploaded '" + file.getOriginalFilename() + "'");
redirectAttributes.addFlashAttribute("path",
"file path url '" + path + "'");
} catch (Exception e) {
logger.error("upload file failed",e);
}
return "redirect:/uploadStatus";
}
/**
* 页面跳转
* @return
*/
@GetMapping("/uploadStatus")
public String uploadStatus() {
return "uploadStatus";
}
/**
* 上传文件
* @param multipartFile
* @return
* @throws IOException
*/
public String saveFile(MultipartFile multipartFile) throws Exception {
String[] fileAbsolutePath={};
String fileName=multipartFile.getOriginalFilename();
String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
byte[] file_buff = null;
InputStream inputStream=multipartFile.getInputStream();
if(inputStream!=null){
int len1 = inputStream.available();
file_buff = new byte[len1];
inputStream.read(file_buff);
}
inputStream.close();
FastDFSFile file = new FastDFSFile(fileName, file_buff, ext);
try {
//上传文件
fileAbsolutePath = FastDFSClient.upload(file);
} catch (Exception e) {
logger.error("upload file Exception!",e);
}
if (fileAbsolutePath==null) {
logger.error("upload file failed,please upload again!");
}
String path=FastDFSClient.getTrackerUrl()+fileAbsolutePath[0]+ "/"+fileAbsolutePath[1];
return path;
}
}
添加前端页面
upload.html
<!DOCTYPE html>
<html>
<body>
<h1>Spring Boot file upload example</h1>
<form method="POST" action="/upload" enctype="multipart/form-data">
<input type="file" name="file" /><br/><br/>
<input type="submit" value="Submit" />
</form>
</body>
</html>
uploadStatus.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
<h1>Spring Boot - Upload Status</h1>
<div th:if="${message}">
<h2 th:text="${message}"/>
</div>
<div th:if="${path}">
<h2 th:text="${path}"/>
</div>
</body>
</html>
测试