简介
首先简单了解一下基础概念,FastDFS是一个开源的轻量级分布式文件系统,由跟踪服务器(tracker server)、存储服务器(storage server)和客户端(client)三个部分组成,主要解决了海量数据存储问题,特别适合以中小文件(建议范围:4KB < file_size <500MB)为载体的在线服务。FastDFS的系统结构图如下:
如上图,FastDFS的两个核心概念分别是:
- Tracker(跟踪器)
- Storage(存储节点)
Tracker主要做调度工作,相当于mvc中的controller的角色,在访问上起负载均衡的作用。跟踪器和存储节点都可以由一台或多台服务器构成,跟踪器和存储节点中的服务器均可以随时增加或下线而不会影响线上服务,其中跟踪器中的所有服务器都是对等的,可以根据服务器的压力情况随时增加或减少。Tracker负责管理所有的Storage和group,每个storage在启动后会连接Tracker,告知自己所属的group等信息,并保持周期性的心跳,tracker根据storage的心跳信息,建立group==>[storage server list]的映射表,Tracker需要管理的元信息很少,会全部存储在内存中;另外tracker上的元信息都是由storage汇报的信息生成的,本身不需要持久化任何数据,这样使得tracker非常容易扩展,直接增加tracker机器即可扩展为tracker cluster来服务,cluster里每个tracker之间是完全对等的,所有的tracker都接受stroage的心跳信息,生成元数据信息来提供读写服务。
Storage采用了分卷[Volume](或分组[group])的组织方式,存储系统由一个或多个组组成,组与组之间的文件是相互独立的,所有组的文件容量累加就是整个存储系统中的文件容量。一个卷[Volume](组[group])可以由一台或多台存储服务器组成,一个组中的存储服务器中的文件都是相同的,组中的多台存储服务器起到了冗余备份和负载均衡的作用,数据互为备份,存储空间以group内容量最小的storage为准,所以建议group内的多个storage尽量配置相同,以免造成存储空间的浪费。更多原理性的内容可以参考这篇blog,介绍的很详细:
接下来就具体看一下FastDFS的整个下载安装过程~下载
目前作者最后一次releases的时间的14年11月22号,对应的最新版本是5.0.5,直接在余大的GitHub上下载就可以了:
如上图,由于FastDFS是纯C语言实现,只支持Linux、FreeBSD等UNIX系统,所以我们直接下载tar.gz的压缩包,同时FastDFS 5.0.5同以前版本相比将公共的一些函数等单独封装成了libfastcommon包,所以在安装FastDFS之前我们还必须安装libfastcommon,在余大的GitHub首页可以看到:
下载完成后将下面这两个文件上传至CentOS服务器,然后就可以开始解压安装了:
安装
libfastcommon
首先第一步是安装libfastcommon,我这里将libfastcommon上传到的/usr/local目录下,直接解压:
unzip libfastcommon-master.zip
解压成功后进入目录看一下压缩包的文件:
如果没有装解压工具unzip可以通过以下yum命令进行安装后再解压:
yum -y install unzip zip
解压完成后就可以进行编译安装了,分别执行./make.sh
和./make.sh install
,由于是新安装的系统有可能会提示找不到gcc命令:
如上图,所以我们先要安装gcc编译器:
yum -y install gcc-c++
看到如下信息说明gcc已经安装成功:
此时再次执行./make.sh
命令进行编译,没有error信息的话就说明编译成功了,最后再执行./make.sh install
进行安装,看到类似如下提示信息就说明libfastcommon已安装成功(画风突变,黑底白字看的眼睛累T_T,所以换成白底黑字了):
至此libfastcommon就已经安装成功了,但注意一下上图中红色框标注的内容,libfastcommon.so 默认安装到了/usr/lib64/libfastcommon.so,但是FastDFS主程序设置的lib目录是/usr/local/lib,所以此处需要重新设置软链接(类似于Windows的快捷方式):
ln -s /usr/lib64/libfastcommon.so /usr/local/lib/libfastcommon.soln -s /usr/lib64/libfastcommon.so /usr/lib/libfastcommon.soln -s /usr/lib64/libfdfsclient.so /usr/local/lib/libfdfsclient.soln -s /usr/lib64/libfdfsclient.so /usr/lib/libfdfsclient.so
设置完毕后就可以开始安装fastdfs了。
FastDFS
第一步依然是解压:
tar -zxvf fastdfs-5.05.tar.gz
解压完成后进入目录fastdfs-5.05,依次执行./make.sh和./make.sh install:
./make.sh./make.sh install
没有报错就说明安装成功了,在log中我们可以发现安装路径:
没错,正是安装到了/etc/fdfs中,我们看一下该目录下的文件:
如上图,安装成功后就会生成如上的3个.sample文件(示例配置文件),我们再分别拷贝出3个后面用的正式的配置文件:
cp client.conf.sample client.confcp storage.conf.sample storage.confcp tracker.conf.sample tracker.conf
之后再查看一下/etc/fdfs的文件目录:
至此FastDFS已经安装完毕,接下来的工作就是依次配置Tracker和Storage了。
Tracker
在配置Tracker之前,首先需要创建Tracker服务器的文件路径,即用于存储Tracker的数据文件和日志文件等,我这里选择在/opt目录下创建一个fastdfs_tracker目录用于存放Tracker服务器的相关文件:
mkdir /opt/fastdfs_tracker
- 1
接下来就要重新编辑上一步准备好的/etc/fdfs目录下的tracker.conf配置文件,打开文件后依次做以下修改:
- disabled=false #启用配置文件(默认启用)
- port=22122 #设置tracker的端口号,通常采用22122这个默认端口
- base_path=/opt/fastdfs_tracker #设置tracker的数据文件和日志目录
- http.server_port=6666 #设置http端口号,默认为8080
配置完成后就可以启动Tracker服务器了,但首先依然要为启动脚本创建软引用,因为fdfs_trackerd等命令在/usr/local/bin中并没有,而是在/usr/bin路径下:
ln -s /usr/bin/fdfs_trackerd /usr/local/binln -s /usr/bin/stop.sh /usr/local/binln -s /usr/bin/restart.sh /usr/local/bin
最后通过命令启动Tracker服务器:
service fdfs_trackerd start
命令执行后可以看到以下提示:
如果启动命令执行成功,那么同时在刚才创建的tracker文件目录/opt/fastdfs_tracker中就可以看到启动后新生成的data和logs目录,tracker服务的端口也应当被正常监听,最后再通过netstat命令查看一下端口监听情况:
netstat -unltp|grep fdfs
可以看到tracker服务运行的22122端口正常被监听:
确认tracker正常启动后可以将tracker设置为开机启动,打开/etc/rc.d/rc.local并在其中加入以下配置:
service fdfs_trackerd start
如果重启后发现未能自动启动则通过命令ll /etc/rc.d/rc.local
检查一下rc.local是否具备可执行权限,若是无可执行权限则通过chmod +x /etc/rc.d/rc.local
进行授权,如下图:
Tracker至此就配置好了,接下来就可以配置FastDFS的另一核心——Storage。
Storage
同理,步骤基本与配置Tracker一致,首先是创建Storage服务器的文件目录,需要注意的是同Tracker相比我多建了一个目录,因为Storage还需要一个文件存储路径,用于存放接收的文件:
mkdir /opt/fastdfs_storagemkdir /opt/fastdfs_storage_data
接下来修改/etc/fdfs目录下的storage.conf配置文件,打开文件后依次做以下修改:
- disabled=false #启用配置文件(默认启用)
- group_name=group1 #组名,根据实际情况修改
- port=23000 #设置storage的端口号,默认是23000,同一个组的storage端口号必须一致
- base_path=/opt/fastdfs_storage #设置storage数据文件和日志目录
- store_path_count=1 #存储路径个数,需要和store_path个数匹配
- store_path0=/opt/fastdfs_storage_data #实际文件存储路径
- tracker_server=192.168.111.11:22122 #tracker 服务器的 IP地址和端口号,如果是单机搭建,IP不要写127.0.0.1,否则启动不成功(此处的ip是我的CentOS虚拟机ip)
- http.server_port=8888 #设置 http 端口号
配置完成后同样要为Storage服务器的启动脚本设置软引用:
ln -s /usr/bin/fdfs_storaged /usr/local/bin
接下来就可以启动Storage服务了:
service fdfs_storaged start
命令执行后可以看到以下提示:
同理,如果启动成功,/opt/fastdfs_storage中就可以看到启动后新生成的data和logs目录,端口23000也应被正常监听,还有一点就是文件存储路径下会生成多级存储目录,那么接下来看看是否启动成功了:
如上图,貌似没成功啊,因为启动storage后文件都没生成,为了确认我们看一下storage的端口情况:
果然是没启动成功!端口目前还是只监听了一个,storage的23000端口并未被监听,那么我们只能去日志文件中找原因了,进入/opt/fastdfs_storage/logs目录下并打开storaged.log文件:
如上图,可以看到确实有一个error,关键信息是:
ERROR - file: storage_func.c, line: 896, mkdir “/etc/fastdfs_storage_data/data” fail, errno: 2, error info: No such file or directory
没有文件或目录!再回头看一下/etc/fdfs目录下的storage.conf的配置:
果不其然配错了,我们指定成了/etc/fastdfs_storage_data目录,而实际上我们创建的位置是/opt/fastdfs_storage_data,粗心大意,老眼昏花!修改路径后再次重启storage服务,然后再看看data目录和实际存储文件的/opt/fastdfs_storage_data:
如上图,可以看到/opt/fastdfs_storage/data目录下生成好的pid文件和dat文件,那么再看一下实际文件存储路径下是否有创建好的多级目录呢:
如上图,没有任何问题,data下有256个1级目录,每级目录下又有256个2级子目录,总共65536个文件,新写的文件会以hash的方式被路由到其中某个子目录下,然后将文件数据直接作为一个本地文件存储到该目录中。那么最后我们再看一下storage服务的端口监听情况:
如上图,可以看到此时已经正常监听tracker的22122端口和storage的23000端口,至此storage服务器就已经配置完成,确定了storage服务器启动成功后,还有一项工作就是看看storage服务器是否已经登记到 tracker服务器(也可以理解为tracker与storage是否整合成功),运行以下命令:
/usr/bin/fdfs_monitor /etc/fdfs/storage.conf
[root /]# /usr/bin/fdfs_monitor /etc/fdfs/storage.conf
[2016-09-23 12:59:26] DEBUG - base_path=/opt/fastdfs_storage, connect_timeout=30, network_timeout=60, tracker_server_count=1, anti_steal_token=0, anti_steal_secret_key length=0, use_connection_pool=0, g_connection_pool_max_idle_time=3600s, use_storage_id=0, storage server id count: 0server_count=1, server_index=0
tracker server is 192.168.111.11:22122
group count: 1
Group 1:
group name = group1 disk total space = 6818 MB disk free space = 2169 MB trunk free space = 0 MB storage server count = 1 active server count = 1 storage server port = 23000 storage HTTP port = 8888 store path count = 1 subdir count per path = 256 current write server index = 0 current trunk file id = 0Storage 1:
id = 192.168.111.11 ip_addr = 192.168.111.11 ACTIVE http domain = version = 5.05 join time = 2016-09-23 11:15:54 up time = 2016-09-23 12:33:26 total storage = 6818 MB free storage = 2169 MB upload priority = 10 store_path_count = 1 subdir_count_per_path = 256 storage_port = 23000 storage_http_port = 8888 current_write_path = 0 source storage id = if_trunk_server = 0 connection.alloc_count = 256 connection.current_count = 0 connection.max_count = 0 total_upload_count = 0 success_upload_count = 0 total_append_count = 0 success_append_count = 0 total_modify_count = 0 success_modify_count = 0 total_truncate_count = 0 success_truncate_count = 0 total_set_meta_count = 0 success_set_meta_count = 0 total_delete_count = 0 success_delete_count = 0 total_download_count = 0 success_download_count = 0 total_get_meta_count = 0 success_get_meta_count = 0 total_create_link_count = 0 success_create_link_count = 0 total_delete_link_count = 0 success_delete_link_count = 0 total_upload_bytes = 0 success_upload_bytes = 0 total_append_bytes = 0 success_append_bytes = 0 total_modify_bytes = 0 success_modify_bytes = 0 stotal_download_bytes = 0 success_download_bytes = 0 total_sync_in_bytes = 0 success_sync_in_bytes = 0 total_sync_out_bytes = 0 success_sync_out_bytes = 0 total_file_open_count = 0 success_file_open_count = 0 total_file_read_count = 0 success_file_read_count = 0 total_file_write_count = 0 success_file_write_count = 0 last_heart_beat_time = 2016-09-23 12:58:59 last_source_update = 1970-01-01 08:00:00 last_sync_update = 1970-01-01 08:00:00 last_synced_timestamp = 1970-01-01 08:00:00
如上所示,看到192.168.111.11 ACTIVE 字样即可说明storage服务器已经成功登记到了tracker服务器,同理别忘了添加开机启动,打开/etc/rc.d/rc.local并将如下配置追加到文件中:
service fdfs_storaged start
至此我们就已经完成了fastdfs的全部配置,此时也就可以用客户端工具进行文件上传下载的测试了。
初步测试
测试时需要设置客户端的配置文件,编辑/etc/fdfs目录下的client.conf 文件,打开文件后依次做以下修改:
- base_path=/opt/fastdfs_tracker #tracker服务器文件路径
- tracker_server=192.168.111.11:22122 #tracker服务器IP地址和端口号
- http.tracker_server_port=6666 # tracker 服务器的 http 端口号,必须和tracker的设置对应起来
配置完成后就可以模拟文件上传了,先给/opt目录下放一张图片(暴雪爸爸的LOGO):
然后通过执行客户端上传命令尝试上传:
/usr/bin/fdfs_upload_file /etc/fdfs/client.conf /opt/BLIZZARD.jpg
- 1
运行后可以发现给我们返回了一个路径:
这就表示我们的文件已经上传成功了,当文件存储到某个子目录后,即认为该文件存储成功,接下来会为该文件生成一个文件名,文件名由group、存储目录、两级子目录、fileid、文件后缀名(由客户端指定,主要用于区分文件类型)拼接而成,如下图:
同时在之前配置的storage服务器的实际文件存储路径中也可以根据返回的路径找到实际文件:
接下来尝试用浏览器发送HTTP请求访问一下文件:
此时发现并不能访问,因为FastDFS目前已不支持http协议,我们在FastDFS 4.0.5的版本更新日志中可以看到这样一条信息:
如上图,4.0.5版本开始移除了自带的HTTP支持(因为之前自带的HTTP服务较为简单,无法提供负载均衡等高性能服务),所以余大提供了nginx上使用FastDFS的模块fastdfs-nginx-module,下载地址如下:,这样做最大的好处就是提供了HTTP服务并且解决了group中storage服务器的同步延迟问题,接下来就具体记录一下fastdfs-nginx-module的安装配置过程。
fastdfs-nginx-module
在余大的GitHub上下载好fastdfs-nginx-module上传到我们的CentOS中就可以开始安装了,在安装nginx之前需要先安装一些模块依赖的lib库,我在以前写的blog有做详细介绍(),直接贴出安装代码:
yum -y install pcre pcre-devel yum -y install zlib zlib-devel yum -y install openssl openssl-devel
依次装好这些依赖之后就可以开始安装nginx了。
storage nginx
首先是为storage服务器安装nginx,首先将nginx和fastdfs-nginx-module的安装包上传至CentOS:
首先分别进行解压:
tar -zxvf nginx-1.8.1.tar.gzunzip fastdfs-nginx-module-master.zip
解压成功后就可以编译安装nginx了,进入nginx目录并输入以下命令进行配置:
./configure --prefix=/usr/local/nginx --add-module=/usr/local/fastdfs-nginx-module-master/src
配置成功后会看到如下信息:
紧接着就可以进行编译安装了,依次执行以下命令:
makemake install
安装完成后,我们在我们指定的目录/usr/local/nginx中就可以看到nginx的安装目录了:
接下来要修改一下nginx的配置文件,进入conf目录并打开nginx.conf文件加入以下配置:
listen 9999;location ~/group1/M00 { root /opt/fastdfs_storage_data/data; ngx_fastdfs_module;}
然后进入FastDFS的安装目录/usr/local/fastdfs-5.05目录下的conf目录,将http.conf和mime.types拷贝到/etc/fdfs目录下:
cp -r /usr/local/fastdfs-5.05/conf/http.conf /etc/fdfs/cp -r /usr/local/fastdfs-5.05/conf/mime.types /etc/fdfs/
接下来还需要把fastdfs-nginx-module安装目录中src目录下的mod_fastdfs.conf也拷贝到/etc/fdfs目录下:
cp -r /usr/local/fastdfs-nginx-module-master/src/mod_fastdfs.conf /etc/fdfs/
看一下/etc/fdfs目录下当前所有的配置文件:
没什么问题,接下来就需要编辑刚拷贝的这个mod_fastdfs.conf文件了,打开mod_fastdfs.conf并按顺序依次编译以下内容:
- base_path=/opt/fastdfs_storage #保存日志目录
- tracker_server=192.168.111.11:22122 #tracker服务器的IP地址以及端口号
- storage_server_port=23000 #storage服务器的端口号
- url_have_group_name = true #文件 url 中是否有 group 名
- store_path0=/opt/fastdfs_storage_data # 存储路径
- group_count = 3 #设置组的个数,事实上这次只使用了group1
设置了group_count = 3,接下来就需要在文件尾部追加这3个group setting:
[group1]group_name=group1storage_server_port=23000store_path_count=1store_path0=/opt/fastdfs_storage_data[group2]group_name=group2storage_server_port=23000store_path_count=1store_path0=/opt/fastdfs_storage_data[group3]group_name=group3storage_server_port=23000store_path_count=1store_path0=/opt/fastdfs_storage_data
接下来还需要建立 M00 至存储目录的符号连接:
ln -s /opt/fastdfs_storage_data/data /opt/fastdfs_storage_data/data/M00
最后启动nginx:
/usr/local/nginx/sbin/nginx
显示如下信息说明nginx已启动成功:
通过浏览器也可以看到nginx的主页:
storage服务器的nginx就已经安装完毕,接下来看一下tracker服务器的nginx安装。
tracker nginx
同理,再装一个nginx,目录命名为nginx2,安装路径依旧放在/usr/local下,由于和之前一样,此处就不再做详细解释:
mkdir nginx2cd nginx-1.8.1/./configure --prefix=/usr/local/nginx2 --add-module=/usr/local/fastdfs-nginx-module-master/srcmakemake install
接下来依然是修改nginx2的配置文件,进入conf目录并打开nginx.conf文件加入以下配置,tracker的nginx无需修改listen端口,即默认的80端口,并将upstream指向storage的nginx地址:
upstream fdfs_group1 { server 127.0.0.1:9999;}location /group1/M00 { proxy_pass http://fdfs_group1;}
接下来启动nginx2:
/usr/local/nginx2/sbin/nginx
此时访问nginx2的主页,由于没有修改端口,直接访问ip地址即可:
最后一步就是需要修改/etc/fdfs目录下的client.conf文件,打开该文件并加入以下配置:
base_path=/data/fastdfs_storage #日志存放路径tracker_server=192.168.116.145:22122 #tracker 服务器 IP 地址和端口号http.tracker_server_port=6666 # tracker 服务器的 http 端口号,必须和tracker的设置对应起来
至此关于fastdfs就已经全部配置完毕了,再一次进行测试看看是否能正常上传文件并通过http访问文件。
HTTP测试
再给/opt目录下上传一张暴雪爸爸的LOGO图:
通过客户端命令测试上传:
如上图,依旧上传成功,接下来的关键就是通过HTTP测试文件访问,打开浏览器输入ip地址+文件名看看是否能正常访问该图片:
一切正常~ 至此关于FastDFS在CentOS 7下的部署测试就已经全部完成了。
前言
上一篇博客记录了FastDFS v5.0.5在Linux CentOS 7中的安装与配置(),本篇blog主要记录一下SpringMVC整合FastDFS的java客户端实现web中的文件上传与下载。
下载编译
在余大的GitHub上可以下载到fastdfs-client-java的源代码:
如上图,这个版本是通过JDK1.5编译的,根据需求可以通过源码重新编译jar包,我这里将原项目的maven编译插件的版本改为JDK 1.7之后重新进行了编译,编译安装成功后可以在我们本地的maven仓库看到fastdfs-client-java的jar包:
最后在我们项目的pom中添加fastdfs-client-java的坐标信息就OK了:
org.csource fastdfs-client-java 1.25
文件上传
首先来实现文件上传,fastdfs-client-java的上传是通过传入一个byte[ ]来完成的,简单看一下源码:
public String[] upload_file(byte[] file_buff, String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException{ final String group_name = null; return this.upload_file(group_name, file_buff, 0, file_buff.length, file_ext_name, meta_list);}
如上所示,暂且不再深入研究原理,此处我们知道需要一个byte[ ]类型的参数就可以了,而SpringMVC的文件上传用到的MultipartFile对象可以直接通过getBytes方法得到文件的byte[ ],也就是CommonsMultipartFile类中的getBytes(),源码如下:
@Overridepublic byte[] getBytes() { if (!isAvailable()) { throw new IllegalStateException("File has been moved - cannot be read again"); } byte[] bytes = this.fileItem.get(); return (bytes != null ? bytes : new byte[0]);}
那么接下来我们就知道如何上传了,当然首先需要做一些简单的封装,这里把文件上传的相关属性封装在了一个接口中,需要用到文件上传的相关实体或者工具类直接实现这个接口即可:
public interface FileManagerConfig extends Serializable { public static final String FILE_DEFAULT_AUTHOR = "WangLiang"; public static final String PROTOCOL = "http://"; public static final String SEPARATOR = "/"; public static final String TRACKER_NGNIX_ADDR = "192.168.0.68"; public static final String TRACKER_NGNIX_PORT = ""; public static final String CLIENT_CONFIG_FILE = "fdfs_client.conf";}
接下来定义FastDFS文件的实体类:
package com.wl.bean;/** * 类概要: FastDFS文件实体 * 创建时间: 2016-9-27 下午10:29:25 * * @Project springmvc-main(com.wl.bean) * @author Wang Liang * @version 1.0.0 */public class FastDFSFile implements FileManagerConfig { private static final long serialVersionUID = 1L; private byte[] content; private String name; private String ext; private String length; private String author = FILE_DEFAULT_AUTHOR; public FastDFSFile(byte[] content, String ext) { this.content = content; this.ext = ext; } public FastDFSFile(byte[] content, String name, String ext) { this.content = content; this.name = name; this.ext = ext; } public FastDFSFile(byte[] content, String name, String ext, String length, String author) { this.content = content; this.name = name; this.ext = ext; this.length = length; this.author = author; } public byte[] getContent() { return content; } public void setContent(byte[] content) { this.content = content; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getExt() { return ext; } public void setExt(String ext) { this.ext = ext; } public String getLength() { return length; } public void setLength(String length) { this.length = length; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; }}
如上所示,包括上传所必须的file_buff和file_ext_name以及在meta_list中存放的几个文件描述属性。接下来看一下核心工具类FileManager:
import java.io.File;import java.io.IOException;import org.csource.common.NameValuePair;import org.csource.fastdfs.ClientGlobal;import org.csource.fastdfs.StorageClient;import org.csource.fastdfs.StorageServer;import org.csource.fastdfs.TrackerClient;import org.csource.fastdfs.TrackerServer;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.ResponseEntity;/** * 类概要: FastDFS Java客户端工具类 * 创建时间: 2016-9-26 上午10:26:48 * * @Project springmvc-main(com.wl.bean) * @author Wang Liang * @version 1.0.0 */public class FileManager implements FileManagerConfig { private static final long serialVersionUID = 1L; private static TrackerClient trackerClient; private static TrackerServer trackerServer; private static StorageServer storageServer; private static StorageClient storageClient; static { try { String classPath = new File(FileManager.class.getResource("/").getFile()).getCanonicalPath(); String fdfsClientConfigFilePath = classPath + File.separator + CLIENT_CONFIG_FILE; ClientGlobal.init(fdfsClientConfigFilePath); trackerClient = new TrackerClient(); trackerServer = trackerClient.getConnection(); storageClient = new StorageClient(trackerServer, storageServer); } catch (Exception e) { e.printStackTrace(); } } /** * 方法概要: 文件上传 * 创建时间: 2016-9-26 上午10:26:11 * * @param FastDFSFile * file * @return fileAbsolutePath * @author Wang Liang */ public static String upload(FastDFSFile file,NameValuePair[] valuePairs) { String[] uploadResults = null; try { uploadResults = storageClient.upload_file(file.getContent(),file.getExt(), valuePairs); } catch (Exception e) { e.printStackTrace(); } String groupName = uploadResults[0]; String remoteFileName = uploadResults[1]; String fileAbsolutePath = PROTOCOL + TRACKER_NGNIX_ADDR //+ trackerServer.getInetSocketAddress().getHostName() //+ SEPARATOR + TRACKER_NGNIX_PORT + SEPARATOR + groupName + SEPARATOR + remoteFileName; return fileAbsolutePath; }}
如上所示,在类初始化时加载fdfs_client.conf配置文件并构造tracker server和storage server,文件上传是通过storageClient.upload_file
方法来实现的,而返回的uploadResults字符串数组正是文件名,固定两个元素,uploadResults[0]是组名(group),而uploadResults[1]就是组名后面的文件全名了,最后我们的方法中有做了部分拼接使得FileManager.upload直接可以返回完成的文件路径,下面就是我们调用上传方法的controller中的方法了:
@RequestMapping(value = "/add", method = RequestMethod.POST)public String add(@Validated User user, BindingResult br,MultipartFile attach, HttpServletRequest request) throws IOException, MyException { if (br.hasErrors()) { return "user/add"; } // 获取文件后缀名 String ext = attach.getOriginalFilename().substring(attach.getOriginalFilename().lastIndexOf(".")+1); FastDFSFile file = new FastDFSFile(attach.getBytes(),ext); NameValuePair[] meta_list = new NameValuePair[4]; meta_list[0] = new NameValuePair("fileName", attach.getOriginalFilename()); meta_list[1] = new NameValuePair("fileLength", String.valueOf(attach.getSize())); meta_list[2] = new NameValuePair("fileExt", ext); meta_list[3] = new NameValuePair("fileAuthor", "WangLiang"); String filePath = FileManager.upload(file,meta_list); user.setFilePath(filePath); users.put(user.getUsername(), user); return "redirect:/user/users";}
如上所示,首先通过字符串截取得到上传文件的后缀名,然后通过文件后缀和文件的byte[ ]构造FastDFSFile对象,接着构造meta_list的NameValuePair[] 数组,这里主要是对文件的可选性描述信息,最后通过FileManager.upload即可完成上传并返回该文件的绝对访问路径,可以根据需要存入DB或文件等等,没有报异常就说明文件上传成功,接下来看看文件下载。
文件下载
fastdfs-client-java提供的文件下载的api需要两个参数,分别是group_name(组名)和remote_filename(文件名),源码如下:
/*** download file from storage server* @param group_name the group name of storage server* @param remote_filename filename on storage server* @return file content/buff, return null if fail*/public byte[] download_file(String group_name, String remote_filename) throws IOException, MyException{ final long file_offset = 0; final long download_bytes = 0; return this.download_file(group_name, remote_filename, file_offset, download_bytes);}
所以我们仅需在这里得到group_name和remote_filename即可,因为之前我们在文件上传时候已经保存了图片的绝对路径(user.setFilePath(filePath)
),所以在此处仅需要获取到绝对路径并进行字符串的拆分截取即可,接下来先看一下封装在FileManager中的下载方法:
/** * 方法概要: 文件下载 * 创建时间: 2016-9-26 上午10:28:21 * * @param String * groupName * @param String * remoteFileName * @return returned value comment here * @author Wang Liang */public static ResponseEntitydownload(String groupName, String remoteFileName,String specFileName) { byte[] content = null; HttpHeaders headers = new HttpHeaders(); try { content = storageClient.download_file(groupName, remoteFileName); headers.setContentDispositionFormData("attachment", new String(specFileName.getBytes("UTF-8"),"iso-8859-1")); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return new ResponseEntity (content, headers, HttpStatus.CREATED);}
如上所示,17行调用fastdfs-client-java提供的下载方法,下载成功后返回的是一个byte[ ],刚好结合SpringMVC官方推荐的构造HttpEntity的方式即可实现文件下载,这个download方法我多指定了一个参数specFileName目的是保证给客户端看到的下载后的文件名是通过程序来自定义的,而不是fastdfs服务器上的那一长串默认字符串,18行指定了utf-8编码使得我们自定义的文件名支持中文,这一点很重要,否则将无法正确下载我们重命名后的包含中文的文件。最后在看一下controller中的下载方法:
@RequestMapping(value = "/{username}/download", method = RequestMethod.GET)public ResponseEntitydownload(@PathVariable String username, Model model,HttpServletResponse response) throws IOException, MyException { User u = users.get(username); String filePath = u.getFilePath(); String substr = filePath.substring(filePath.indexOf("group")); String group = substr.split("/")[0]; String remoteFileName = substr.substring(substr.indexOf("/")+1); String specFileName = username + substr.substring(substr.indexOf(".")); return FileManager.download(group, remoteFileName,specFileName);}
同我们之前的想法一样,截取文件的绝对路径分别得到group_name以及file_name,而传入的specFileName我们这里自定义为用户名(username)+截取后的文件后缀名,看一下效果:
如上图,点击【下载附件】,即可正确下载以及重命名文件,至此SpringMVC结合fastdfs的文件上传下载就已全部结束了。