IT评测·应用市场-qidao123.com

标题: 爆肝两万字,详解fastdfs分布式文件系统 [打印本页]

作者: 万有斥力    时间: 2023-3-9 22:41
标题: 爆肝两万字,详解fastdfs分布式文件系统
1.学习目标


2.简介

技术论坛:http://bbs.chinaunix.net/forum-240-1.html
资源地址:https://sourceforge.net/projects/fastdfs/
源码地址:https://github.com/happyfish100
FastDFS中的文件标识分为两个部分:卷名和文件名,二者缺一不可。
2.1.架构图


解释:
写入
假设我现在client要上传文件,我要找到跟踪器tracker,tracker找到client,然后tracker找到存储节点,看看存储节点那个卷下面的节点比较空闲,能放得下这个文件,然后写入进去,生成一个文件名
读取
如果我们client要下载文件,不需要与tracker再做交互,直接与storage打交道,根据我们当时上传文件tracker给我们提供的文件名,我们加上服务器名字和端口号再加上完整的文件名即可读取下载成功
主备切换
我们storage存储节点是由多个卷构成的一个大的集群,比方说c d e盘构成一个硬盘,每个卷又分成主和子,他们两者文件类型一致,如果主服务器崩了,子服务器马上可以顶上
2.2.上传流程


2.3.下载流程


client可以直接去到Storage进行在线的读取和下载,这个在线读取和下载前提是我们知道它的一个ip地址和端口号,后面跟上卷名,再跟上文件名,我们如果知道这个完整路径的话,可以直接去到我们存储节点进行在线预览,或者是在线下载;如果我们现在只知道一个卷名和文件名,我们并不知道ip和端口,我们也可以去找我们的tracker,拿着我们的卷名和文件名去找我们的跟踪器,跟踪器就会找到对应的卷名和文件名所在的节点,会把这个存储节点的ip地址和端口号返回给我们的客户端,然后我们client再次通过我们的ip端口卷名文件名,然后直接通过我们的存储节点进行我们的读取和下载操作,一般我们如果考虑效率问题的话,肯定是我们直接拿着ip端口卷名文件名直接去存储节点,读取我们的一个文件,如果实在是不知道ip和端口情况下,可能就需要通过我们tracker,但一般情况下,我们tracker上传的时候,我们tracker会返回一个完整的卷名和文件名加ip和端口,我们一般呢会把返回的相对路径存放到数据库里面去,那我们需要进行文件预览和下载的时候呢,我们一般从数据库拿到我们带有ip和端口的卷名和文件名这一整串信息的数据直接去storage进行一个下载 ,效率更高一点!!!
2.4.术语介绍

2.5.同步机制

2.6.FastDFS运行时目录结构

2.6.1.Tracker Server目录

2.7.FastDFS和其它文件存储的简单对比

2.7.1.FastDFS和集中存储方式对比

指标FastDFSNFS集中存储设备加 NetApp、NAS线性扩容性高【扩容好】差差文件高并发访问性能高【速度快】差一般文件访问方式专用API【有自己的AIP】POSIX【可移植操作系统接口】POSIX硬件成本较低【成本低】中等【硬盘成本高】高【硬盘成本高】相同内容文件只保存一份支持【相同文件只有只保留一份】不支持不支持2.7.2.FastDFS和mogileFS对比

指标FastDFSmogileFS系统简洁性简洁 只有两个角色:tracker和storage一般有三个角色:trocker、storage和存储文件信息的mysql db系统性能很高(没有使用数据库,文件同步直接点对点,不经过tracker中转)高(使用mysql来存储文件索引信息,文件同步通过tracker调度和中转)系统稳定性高(C语言开发,可以支持高并发和高负载)一般(Perl语言开发,高并发和高负载支持一般)【没有C语言高并发高负载好】RAID方式分组(组内冗余),灵活性较大动态冗余,灵活性一般通信协议专用协议,下载文件支持http【专用协议最大的好处就是写的操作效率跟高,在tcp/ip之上】http技术文档较详细较少文件附加属性(meta data)支持【文件相关属性存储在storage】不支持相同内容只保存一份支持【根据文件相关属性判断,如果属性一致则覆盖】不支持下载文件时支持文件偏移量【断点续传, 从指定位置向前向后移动的字节数,比如我们下载一个文件一半点暂停,然后再点开始会从你已经下载好的进度开始,而有的文件你点了暂停可能就让你从头开始下载】支持不支持有没有比FastDFS更好的呢?当然有,那就是我们的hdfs,hdfs更多的是大数据方面去用,它的性能会比FastDFS更好一点
3.安装

3.1.安装简介

FastDFS主要是两个角色,一个tracker和storage,它们本质上都是一个FastDFS一个包,它们通过对应不同配置来确认它们不同的角色,所以它们通用的安装都是FastDFS的安装包
我们这边也准备了两台服务器,一台是安装我们tracker,一台安装storage,它们只是对应角色配置不一样

如果电脑配置比较差的话,也可以直接安装在一台服务器上,只需修改对应角色配置即可,安装包都是一样,也可以实现!!!
3.2.FastDFS安装包

上传所选安装包

3.3.安装依赖

3.3.1.安装c++相关依赖

我们fastdfs是根据C语言进行开发的,所以我们需要安装C++相关依赖
  1. yum -y install cmake gcc-c++
复制代码

3.3.2.安装fastdfs核心库

安装完成之后,我们还需要安装我们fastdfs核心库

这个核心库呢其实是从fastdfs和fastdht中提取出来的公用的C函数的库,fastdfs和fastdht是同样一个作者去写的两个产品,都是C语言编写,然后里面会有一些公用函数库,作者把它提取出来当成一个专门核心库
如果我们后缀名是.zip,需要安装一个zip解压插件
  1. yum -y install unzip
复制代码

我们把需要安装的fastdfs所以文件放在一个文件夹方便管理
  1. mkdir -p /usr/local/fastdfs
复制代码
然后我们就可以去解压了
zip后缀
  1. unzip libfastcommon-1.0.43.zip
复制代码
tar后缀
  1. tar -zxvf libfastcommon-1.0.43.zip
复制代码

我们进行之后看见有一个make.sh执行脚本,我们就通过这个脚本去进行一个编译和安装
  1. .make.sh
复制代码
这样就会进行编译

编译好后我们就可以进行安装了
  1. .make.sh install
复制代码

还有一个问题就是我们fastdfs主程序的lib目录是在/usr/local/lib下面的,所以我们需要去创建一些软链接,这些软链接就相当于快捷方式,把它的一个快捷方式从原本指向的目录改成我们想要的指向的目录
这个意思相当把前面原本指向的路径指定到我们后面需要我们指定路径
  1. ln -s /usr/1ib64/1ibfastcommon.so /usr/local/lib/libfastcommon.so
  2. ln -s /usr/local/lib64/libfdfsclient.so /usr/local/lib/libfdfsclient.so
  3. ln -s /usr/local/lib64/libfdfsclient.so /usr/lib/libfdfsclient.so
复制代码
3.3.3.安装fastdfs

  1. tar -zxvf fastdfs-6.06.tar.gz
复制代码
我们发现这里也有make.sh

我们可以做一个可选的操作,就是说我们可以把它的一个路径改成指定的路径,因为它默认安装路径是在/usr下面,我们可以把它改成/usr/local下面去,当然在集群下面就不要去改,那我们现在是安装的一个单节点,可以去尝试的改一下
我们进入fastdfs
  1. vim make.sh
复制代码
我们搜索/TAGE_PREFIX

我们把它改到/usr/local

然后我们再次去安装
  1. ./make.sh 先编译
复制代码

再安装
  1. ./make.sh install
复制代码
安装后,FastDFS主程序所在的位置是:

安装好了之后,我们可以去看一下服务脚本所在位置
  1. cd /etc/init.d/
复制代码

然后我们可以看一下我们配置文件的模板所在位置
  1. cd /etc/fdfs/
复制代码

如果我们这台服务器当做tracker来用的话,只需拷贝tracker配置拷贝过去进行修改,把后面的.sample删掉就可以用了,建议不要拿源文件直接用,最好是拷贝过去再进行修改,因为如果改错了还有备份!!!
我们还可查看内置命令所在目录
  1. cd /usr/local/bin/
复制代码
这个就是fastdfs内置命令,包括重启,启动,停止,还有测试,跟踪等等。。。

到这里,我们整个tracker服务器的fastdfs安装好了!!!
**同理,我们还要去安装storage,拿storage的安装和tracker是一模一样的,只是配置文件不同,其它安装包都一样,所以我们如果在同一台服务器部署只需修改配置文件即可!!! **
4.配置tracker

4.1.拷贝tracker.conf.sample

我们首先进入配置文件模板
  1. cd /etc/fdfs
复制代码
我们看到里有个tracker.conf.sample

然后我们拷贝一下,到conf就行
  1. cp tacker..sample tracker.conf
复制代码
4.2.配置tracker.conf

然后我们就可以进行配置了
  1. vim tracker.conf
复制代码

我们简单了解一下里面的属性
这里我们就配置完成了,我们主要配置base_path 根目录即可!!!
然后我们别忘了创建刚才自定义的目录
  1. mkdir -p /fastdfs/tracker
复制代码
4.3.启动tracker

我们创建完目录呢,就可以启动tracker了,来到启动目录
  1. cd /etc/init.d/
复制代码

我们看到有两个文件 fdfs_trackerd和fdfs_storaged这两个就是我们启动的文件
我们之前安装的时候,我们修改过它的一个目录,所以呢我们这边启动的时候呢,也去修改它的一个目录,如果我们没有去修改它的目录,这边可以直接启动!!!
我们进入配置文件修改目录即可!!!
  1. vim fdfs_trackerd
复制代码
我们修改之后应该是/usr/local/bin/fdfs_trackerd,

保存并退出, 启动即可
  1. ./fdfs_trackerd start
复制代码

怎么去看有没有启动成功呢?
两种方法:
查看状态
  1. ./fdfs_trackerd status
复制代码

查看进程
  1. ps -ef|grep fdfs
复制代码

停止
  1. .fdfs_trackerd stop
复制代码
重启
  1. /etc/init.d/fdfs_trackerd restart
复制代码
开机启动
我们进入文件
  1. vim /etc/rc.d/rc.local
复制代码
添加启动文件
  1. /etc/init.d/fdfs_tracked start
复制代码
4.4.小结

我们现在已经启动了tracker包括我们相应的一些配置,其实真正要做的配置没有,默认的端口都没改,只是把它的base_path 根目录进行了一个修改,当然这个修改也是可有可无的,但是记住一定要创建,有一个负载均衡,大部分默认都是一个轮询的方式,其它的也没什么!!!
5.配置Storage

5.1.拷贝storage.conf.sample

跟tracker一样
  1. cd /etc/fdfs
复制代码

同样,拷贝一份
  1. cp storage.conf.sample storage.conf
复制代码
5.2.配置storage.conf

修改storage
  1. vim storage.conf
复制代码
也是一样,我们观察一下里面的属性

保存并退出即可!!!
创建我们刚才自定义的目录
  1. mkdir -p /fastdfs/storage/base
  2. mkdir -p /fastdfs/storage/store
复制代码
我们安装的时候也修改了目录,所以我们这里也要修改一下
  1. vim /etc/init.d/fdfs_storaged
复制代码
修改PRG=/usr/local/bin/fdfs_storaged

5.3.启动storage

保存并退出,启动storage
  1. /etc/init.d/fdfs_storaged start         
复制代码

查看状态
  1. /etc/init.d/fdfs_storaged status
复制代码

停止
  1. /etc/init.d/fdfs_storaged stop
复制代码
重启
  1. /etc/init.d/fdfs_storaged restart
复制代码
开启启动
我们进入文件
  1. vim /etc/rc.d/rc.local
复制代码
添加启动文件

启动之后我们可以去看看我们刚才创建的两个目录
  1. cd /fastdfs storage/
复制代码

base目录【基础数据目录】
可以看到有一个data和logs 基础数据和日志

可以看到有对应的一个日志

进入data目录
里面有对应我们storage的一个进程号,然后启动的一个数据和**同步相应的相关信息         **
我们回去看我们store
  1. cd ../../store
复制代码
这里也有一个data,这个data存放我们上传文件的目录       

我们发现这边使用16进制,从00一直到FF,一共是256个目录,然后我们每一个目录下面还有256个子目录

我们cd 00 再 ls
我们发现还是有 00 -FF 256个目录

再00就没了!!!再往下这边就会存放我们的文件了,至于我们文件上传上来之后会存放在那个目录下面,这个不需要我们去关系,storage会自己去存放,并且我们的tracker会去把我们的一个完整的卷名加文件名,这个文件名就是从data到00再到00再到下面的具体文件名一整个完整路径返回给我们,我们直接能拿到,它具体存放在着256个目录那个目录这个不需要我们操心,这是storage自定义去完成的,我们这里storage开机启动前提是必须先启动tracker再启动storage,不然会报错,因为我们storage配置文件里面配置了tracker_server,所以我们这边如果想要stroage开机自启,必须设置tracker也开机自启,不然不建议storage自启!!!
6.Client配置【可选】

客户端配置不是必需的,因为客户端配置完相当于用命令行去测试我们的fastdfs,所以我们如果不准备用命令行测试,而是准备用代码测试的话,我们完全可以跳过!!!
我们可以把Client放在tracker和storage任意服务器下,并不影响,把它配置改一下即可!!!
6.1.拷贝client.conf.sample

首先进入我们配置模板
  1. cd /etc/fdfs
复制代码
我们可以看到这边有一个client.conf.sample
同样,我们先拷贝
  1. cp client.conf.sample client.conf
复制代码
6.2.配置client.conf

进入配置文件
  1. vim client.conf
复制代码
可以看下它的属性,更改的地方加粗
到这里,client配置就完成了,我们保存并退出,创建刚刚自定义的目录
  1. mkdir -p /fastdfs/client
复制代码
6.3.上传文件

我们看到root目录下面有个图片,我们可以把这个上传上去
怎么去上传呢?上传的话因为我们安装的时候改过对应的一个目录,我们的命令目录呢在/usr/local/bin
我们上传就是fdfs_upload_file
  1. ./fdfs_upload_file /etc/fdfs/client.conf ~/cat-114782_640 (1).jpg
复制代码
上传命令+客户端配置+上传的文件
返回上传的完整卷名加文件名,它的文件名是重新命名的


我们可以进入storage存放文件目录去查看我们上传的文件

小结

我们要记住M00是一个虚拟目录,有点相当于我们windows的快捷方式,它的引用主要是引用到我们的data目录下面,data就对应M00,快捷方式的一个意思
6.4.删除文件
  1. ./fdfs_delete_file /etc/fdfs/client.conf group1/M00/00/00/wKj4ZWQJdC-AOV6HAAKSU_3XkA0484.jpg
复制代码
删除命令+client配置文件+完整的卷名和文件名
这里要注意,因为我们现在操作没有ip和端口,所以我们需要根据追踪器,根据这一整串完整的文件名去获取stroage的ip和端口,才会去进行一个删除这也就是我们client为什么要去配置tracker服务器
这样则表示删除成功

我们可以进入data-00-00查看是否删除成功
我们发现后最484的文件名没有了

7.安装nginx和fastdfs_nginx_module

为什么要安装nginx呢?因为我们fastdfs是一个文件系统,那可以存放很多类型的文件,比如说存放图片或者其它一个类型,图片的话我们可以通过网页直接去预览,而不需要通过我们现在这个操作,通过client拿到一个完整的卷名加文件名,通过tracker拿到对应的ip地址和端口再去下载预览,太麻烦,我们可以通过http协议直接在我们的url里面输入我们的一个ip端口,卷名文件名直接在浏览器里面可以访问这张图片或预览,那这个时候呢我们fastdfs没法实现,就要安装fastdfs_module_nginx和nginx进行代理

7.1.安装组件fastdfs-nginx-module

解压
  1. tar zxvf fastdfs-nginx-module-1.22
复制代码
cd 进入目录

我们来到src源码目录

源码目录有相应的一个配置,我们需要修改相应的一个配置
  1. vim config
复制代码
为什么要改配置呢?因为我们去安装我们的组件之后呢,会去安装我们的nginx,安装nginx的时候需要把我们的一个module模块加进去,加进去之后会寻找我们的一个fastdfs对应的一个安装的目录,如果目录不正确可能就安装失败了!!!所以我们需要去更改我们的一个目录路径

**修改的时候也有区别,因为我们装fastdfs的时候修改了我们安装目录,所以我们这串目录是改过目录之后的,如果我们没有修改过fastdfs的安装目录的,我们改的是另一个目录 **
  1. /usr/local/include/fastdfs /usr/include/fastcommon/
复制代码
fastdfs位置+核心库,这两个改呢,是因为我们安装改过,如果没有的话呢,我们改的不是这两个,就应该是把local删掉就行了
保存并退出,我们的模块就改好了,改好了之后并不代表它已经安装了,怎么去安装呢,就是我们去安装nginx的时候,把模块添加上去即可,所以我们还要去安装我们的nginx

7.2.安装nginx

依赖
安装之前我们还需要安装对应依赖
  1. yum install -y gcc gcc-c++ make automake autoconf libtool pcre pcre-develzlib zlib-devel
  2. openss1 openss1-devel
复制代码

解压nginx
  1. tar -zxvf nginx-1.16.1.tar.gz
复制代码
查看目录

我们需要去更改一下它的目录
更改目录
先准备一个目录
  1. mkdir -p /var/temp/nginx
复制代码
配置nginx安装信息
  1. ./configure \
  2. --prefix=/usr/local/nginx \
  3. --pid-path=/var/run/nginx/nginx.pid \
  4. --lock-path=/var/lock/nginx.lock \
  5. --error-log-path=/var/log/nginx/error.log \
  6. --http-log-path=/var/log/nginx/access.log \
  7. --with-http_gzip_static_module \
  8. --http-client-body-temp-path=/var/temp/nginx/client \
  9. --http-proxy-temp-path=/var/temp/nginx/proxy \
  10. --http-fastcgi-temp-path=/var/temp/nginx/fastcgi\
  11. --http-uwsgi-temp-path=/var/temp/nginx/uwsgi \
  12. --http-scgi-temp-path=/var/temp/nginx/scgi \
  13. --add-module=/usr/local/fastdfs/fastdfs-nginx-module-1.22/src
复制代码
下面这些信息放入我们刚才创建的文件
这样我们nginx安装信息就配置完成

接下来我们就可以进行安装了
预编译
  1. make
复制代码
编译加安装
  1. make install
复制代码
8.配置nginx模块

8.1.拷贝mod_fastdfs.conf

同理跟上面操作一样我们先把fastdfs_nginx_module模块配置文件修改
  1. cd /usr/local/fastdfs/fastdfs-nginx-module-1.22/src/
复制代码
这里面有一个我们对应的一个配置文件mod_fastdfs.conf
  1. cd mod_fastdfs.conf /etc/fdfs
复制代码

然后我们进行一个相应的修改
8.2.修改mod_fastdfs.conf


保存并退出
8.3.拷贝http配置

这两个文件是因为我们nginx在网页上直接看图片的时候呢,是用的一个http协议,所以我们需要去拷贝一下
http
  1. cp /usr/local/fastdfs/fastdfs-6.06/conf/http.conf /etc/fdfs/
复制代码
请求头
  1. cp /usr/local/fastdfs/fastdfs-6.06/conf/mime.types /etc/fdfs/
复制代码
8.4.创建nginx

8.4.1.启动软连接

因为我们nginx启动的时候呢,会在默认的/usr/lib64目录下面去查找所需的so文件,那因为我们安装fastdfs的时候呢,已经修改了它的目录,所以我们一定要创建它的软链接,不然会失败
  1. ln-s /usr/local/lib64/libfdfsclient.so /usr/lib64/libfdfsclient.so
复制代码
8.4.2.网络服务启动软链接

这个软链接呢,我们的追踪服务返回的里面有一个M00,这个M00其实就是一个data,它是相当于windows的一个快捷方式,那我们需要把这个M00指定到data下面
  1. ln -s /fastdfs/storage/store/data//fastdfs/storage/store/data/M00
复制代码
我们可以去看一下我们创建的软连接是否生效
  1. cd /fastdfs/storage/store/data
复制代码
我们可以看到这边有个M00

它指定的目录呢就是我们data目录下

9.配置nginx

9.1.修改nginx.conf
  1. cd /usr/local/nginx/
复制代码
进入conf目录
  1. cd conf
复制代码
修改nginx.conf
  1. vim nginx.conf       
复制代码

修改哪些东西呢?
加入以下配置
  1. location ~/group[0-9]/M00{
  2.     ngx_fastdfs_module;
  3. }
复制代码
当我们去访问8888端口时候呢,它就会找到我们的本地的group0/M00,然后通过我们的ngx_fastdfs_module这样的一个模块去找到我们storage上面存储的一个对应文件就可以预览了!!!
9.2.重启storage并启动nginx

保存并退出,我们nginx配置就完成了,我们需要重启一下我们的storage并且要启动nginx
  1. /etc/init.d/fdfs_storaged restart
复制代码

我们来到nginx-sbin目录启动nginx
  1. ./nginx
复制代码
看到端口表示启动成功!!!

查看启动状态
  1. ps -ef |grep nginx
复制代码

9.3.上传图片通过网页访问

我们通过客户端上传图片
  1. /usr/local/bin/fdfs_upload_file /etc/fdfs/client.conf  cat-114782_640.jpg
复制代码
同理,**上传命令+client配置+文件名即可        **
查看上传的文件
  1. cd /fastdfs/storage/store/data/00/00/
复制代码
  根据tracker给出的路径通过网页进行访问,ip+端口+卷名和文件名

10.Java API

10.1.依赖
  1. <dependency>
  2.   <groupId>org.csource</groupId>
  3.   <artifactId>fastdfs-client-java</artifactId>
  4.   <version>1.30-SNAPSHOT</version>
  5. </dependency>
复制代码
注意:这边我们maven仓库是没有这个依赖的,我们需要自己去下载然后安装到仓库



10.2.常用类

10.2.1.CLientGlobal【加载配置文件】

用于加载配置文件的公共客户端工具。
常用方法:
注意:使用conf或prooerties进行客户端参数配置时,参数key命名不同。
10.2.2.TrckerClient【跟踪器客户端类型】

跟踪器客户端类型。创建此类型对象时,需传递跟踪器组,就是跟踪器的访问地址信息。无参构造方法默认使用ClientGlobal_tracker_group 常量作为跟踪器组来构造对象。【就是我们上面ClientGlobal定义的常量】
创建对象的方式为:
  1. new TrackerClient();或new TrackerClient(ClientGlobal.g_tracker_group)
复制代码
10.2.3.TrackerServer【跟踪器服务类型】

跟踪器服务类型,此类型的对象是通过跟踪器客户端对象创建的。实质上就是一个FastDFS Tracker Server的链接对象。是代码中与Tracker Server链接的工具
构建对象的方式为:
  1. trackerClient.getTrackerServer();
复制代码
10.2.4.StorageServer【存储服务类型】

存储服务类型。此类型的对象是通过跟踪器客户端对象创建的。实质上就是一个FastDFS Storage Server的链接对象。是代码中与StorageServer链接的工具。获取具体存储服务链接,是由Tracker Server分配的,所以构建存储服务对象时,需要依赖跟踪器服务对象
构建对象的方式为:
  1. trackerClient.getStorage(trackerServer);
复制代码
10.2.5.StorageClient【真正去操作文件的客户端类型,需要传入tracker和storage】

存储客户端类型,此类型对象是通过构造方法创建的。创建时,需传递跟踪服务对象和存储服务对象。此对象实质上就是一个访问FastDFS Storage Server的客户端对象,用于实现文件的读写操作
创建对象的方式为:
  1. new StorageClient(trackerServer,storageServer);
复制代码
常用方法有:
11.Demo

11.1.创建项目

创建fastdfsdemo,加上web和thymeleaf依赖

11.2.导入相关依赖
  1.   org.springframework.boot  spring-boot-starter-thymeleaf  org.springframework.boot  spring-boot-starter-web<dependency>
  2.   <groupId>org.csource</groupId>
  3.   <artifactId>fastdfs-client-java</artifactId>
  4.   <version>1.30-SNAPSHOT</version>
  5. </dependency>    org.springframework.boot  spring-boot-starter-test  test
复制代码
11.3.创建fdfs_client.conf配置文件
  1. #连接超时
  2. connect_timeout = 2
  3. #网络超时
  4. network_timeout = 30
  5. #编码格式       
  6. charset = utf-8
  7. #tracke端口号
  8. http.tracker_http_port = 8888
  9. #防盗链功能
  10. http.anti_steal_token = no
  11. #秘钥
  12. http.secret_key = FastDFS1234567890
  13. #tracker ip :端口号
  14. tracker_server = 192.168.248.101:22122
  15. #连接池配置
  16. connection_pool.enabled = true
  17. connection_pool.max_count_per_entry = 500
  18. connection_pool.max_idle_time = 3600
  19. connection_pool.max_wait_time_in_ms = 1000
复制代码
11.4.加载配置文件,生成服务端和客户端
  1. //日志
  2. private static Logger logger = LoggerFactory.getLogger(FastDFSClient.class);
  3. //初始化FastDFS,ClientGlobal.init方法会读取配置文件,并初始化对应的属性
  4. static {
  5.     try {
  6.         String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
  7.         ClientGlobal.init(filePath);
  8.     } catch (IOException | MyException e) {
  9.         logger.error("FastDFS Client init Fail!", e);
  10.         e.printStackTrace();
  11.     }
  12. }
  13. /**
  14. * 生成Storage客户端
  15. * @return
  16. * @throws IOException
  17. */
  18. private static StorageClient getStorageClient() throws IOException {
  19.     TrackerServer trackerServer = getTrackerServer();
  20. //我们可以通过trackerServer拿到StorageServer,不设置也能拿到StorageClient
  21. return new StorageClient(trackerServer, null);
  22. }
  23. /**
  24. * 生成Tracker服务器端
  25. * @return
  26. * @throws IOException
  27. */
  28. private static TrackerServer getTrackerServer() throws IOException {
  29.     TrackerClient trackerClient = new TrackerClient();
  30. return trackerClient.getTrackerServer();
  31. }
复制代码
11.5.上传、下载、删除、查看文件
  1.    /**
  2.      * 上传
  3.      *
  4.      * @param file 文件对象
  5.      * @return
  6.      */
  7.     private static String[] upload(FastDFSFile file) {
  8.         //打印相关信息
  9.         logger.info("File Name:" + file.getName() + ",File lenght" + file.getContent().length);
  10.         //文件属性信息
  11.         NameValuePair[] meta_list = new NameValuePair[1];
  12.         meta_list[0] = new NameValuePair("author", file.getAuthor());
  13.         //上传时间
  14.         long startTime = System.currentTimeMillis();
  15.         //返回一个String数组
  16.         String[] uploadResults = null;
  17.         StorageClient storageClient = null;
  18.         try {
  19.             //拿到客户端
  20.             storageClient = getStorageClient();
  21.             //文件数组;后缀名;文件属性相关数组
  22.             uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
  23.         } catch (IOException | MyException e) {
  24.             e.printStackTrace();
  25.             //上传失败打印文件名
  26.             logger.error("上传失败 File Name:" + file.getName(), e);
  27.         }
  28.         //上传时间
  29.         logger.info("上传时间:" + (System.currentTimeMillis() - startTime + "ms"));
  30.         //验证上传结果
  31.         if (uploadResults == null && storageClient != null) {//判断上传结果是否为空
  32.             logger.error("上传失败" + storageClient.getErrorCode());//上传失败拿到错误结果
  33.         }
  34.         //上传成功会返回相应信息
  35.         logger.info("上传成功,group_name:" + uploadResults[0] + "remoteFileName:" + uploadResults[1]);//打印卷名和完整名字
  36.         return uploadResults;
  37.     }
  38.     /**
  39.      * 下载文件
  40.      *
  41.      * @param groupName      卷
  42.      * @param remoteFileName 完整文件名
  43.      * @return
  44.      */
  45.     public static InputStream downFile(String groupName, String remoteFileName) {
  46.         try {
  47.             //创建storage客户端对象
  48.             StorageClient storageClient = getStorageClient();
  49.             //调用下载方法,返回一个数组
  50.             byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
  51.             //输入要下载的文件
  52.             InputStream ins = new ByteArrayInputStream(fileByte);
  53.             return ins;
  54.         } catch (IOException | MyException e) {
  55.             logger.error("下载失败", e);
  56.         }
  57.         return null;
  58.     }
  59.     /**
  60.      * 删除文件
  61.      *
  62.      * @param groupName      卷
  63.      * @param remoteFileName 完整文件名
  64.      */
  65.     public static void deleteFile(String groupName, String remoteFileName) {
  66.         try {
  67.             StorageClient storageClient = getStorageClient();
  68.             int i = storageClient.delete_file(groupName, remoteFileName);
  69.             logger.info("删除成功" + i);
  70.         } catch (IOException | MyException e) {
  71.             logger.error("删除失败", e);
  72.         }
  73.     }
  74.     /**
  75.      * 查看文件信息
  76.      * @param groupName      卷名
  77.      * @param remoteFileName 完整文件名
  78.      * @return
  79.      */
  80.     public static FileInfo getFile(String groupName, String remoteFileName) {
  81.         try {
  82.             StorageClient storageClient = getStorageClient();
  83.             //获取文件方法
  84.             FileInfo file_info = storageClient.get_file_info(groupName, remoteFileName);
  85.             return file_info;
  86.         } catch (Exception e) {
  87.             logger.error("查看文件信息失败", e);
  88.         }
  89.         return null;
  90.     }
复制代码
11.6.网页查看图片方法
  1.     /**
  2.      * 获取文件路径,上传至后返回的完整路径
  3.      * @return
  4.      * @throws IOException
  5.      */
  6.     public static String getTrackerUrl() throws IOException {
  7.         return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":8888/";
  8.     }
复制代码
11.7.完整代码
  1. package com.zhang.fastdfsdemo.utils;import com.zhang.fastdfsdemo.pojo.FastDFSFile;import org.csource.common.MyException;import org.csource.common.NameValuePair;import org.csource.fastdfs.*;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 zb * @version 1.0 * @date 2023/3/9 18:22 * @ClassName FastDFSClient * @Description FastDFS工具类 * StorageServer也可以通过Tracker拿到,我们直接那StorageClient即可, * 因为我们后期操作我们的文件,不管是上传、下载、删除都是通过我们storageClient进行操作的 */public class FastDFSClient {    //日志    private static Logger logger = LoggerFactory.getLogger(FastDFSClient.class);    //初始化FastDFS,ClientGlobal.init方法会读取配置文件,并初始化对应的属性    static {        try {            String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();            ClientGlobal.init(filePath);        } catch (IOException | MyException e) {            logger.error("FastDFS Client init Fail!", e);            e.printStackTrace();        }    }    /**     * 生成Storage客户端     *     * @return     * @throws IOException     */    private static StorageClient getStorageClient() throws IOException {        TrackerServer trackerServer = getTrackerServer();        //我们可以通过trackerServer拿到StorageServer,不设置也能拿到StorageClient        return new StorageClient(trackerServer, null);    }    /**     * 生成Tracker服务器端     *     * @return     * @throws IOException     */    private static TrackerServer getTrackerServer() throws IOException {        TrackerClient trackerClient = new TrackerClient();        return trackerClient.getTrackerServer();    }    /**
  2.      * 上传
  3.      *
  4.      * @param file 文件对象
  5.      * @return
  6.      */
  7.     private static String[] upload(FastDFSFile file) {
  8.         //打印相关信息
  9.         logger.info("File Name:" + file.getName() + ",File lenght" + file.getContent().length);
  10.         //文件属性信息
  11.         NameValuePair[] meta_list = new NameValuePair[1];
  12.         meta_list[0] = new NameValuePair("author", file.getAuthor());
  13.         //上传时间
  14.         long startTime = System.currentTimeMillis();
  15.         //返回一个String数组
  16.         String[] uploadResults = null;
  17.         StorageClient storageClient = null;
  18.         try {
  19.             //拿到客户端
  20.             storageClient = getStorageClient();
  21.             //文件数组;后缀名;文件属性相关数组
  22.             uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
  23.         } catch (IOException | MyException e) {
  24.             e.printStackTrace();
  25.             //上传失败打印文件名
  26.             logger.error("上传失败 File Name:" + file.getName(), e);
  27.         }
  28.         //上传时间
  29.         logger.info("上传时间:" + (System.currentTimeMillis() - startTime + "ms"));
  30.         //验证上传结果
  31.         if (uploadResults == null && storageClient != null) {//判断上传结果是否为空
  32.             logger.error("上传失败" + storageClient.getErrorCode());//上传失败拿到错误结果
  33.         }
  34.         //上传成功会返回相应信息
  35.         logger.info("上传成功,group_name:" + uploadResults[0] + "remoteFileName:" + uploadResults[1]);//打印卷名和完整名字
  36.         return uploadResults;
  37.     }
  38.     /**
  39.      * 下载文件
  40.      *
  41.      * @param groupName      卷
  42.      * @param remoteFileName 完整文件名
  43.      * @return
  44.      */
  45.     public static InputStream downFile(String groupName, String remoteFileName) {
  46.         try {
  47.             //创建storage客户端对象
  48.             StorageClient storageClient = getStorageClient();
  49.             //调用下载方法,返回一个数组
  50.             byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
  51.             //输入要下载的文件
  52.             InputStream ins = new ByteArrayInputStream(fileByte);
  53.             return ins;
  54.         } catch (IOException | MyException e) {
  55.             logger.error("下载失败", e);
  56.         }
  57.         return null;
  58.     }
  59.     /**
  60.      * 删除文件
  61.      *
  62.      * @param groupName      卷
  63.      * @param remoteFileName 完整文件名
  64.      */
  65.     public static void deleteFile(String groupName, String remoteFileName) {
  66.         try {
  67.             StorageClient storageClient = getStorageClient();
  68.             int i = storageClient.delete_file(groupName, remoteFileName);
  69.             logger.info("删除成功" + i);
  70.         } catch (IOException | MyException e) {
  71.             logger.error("删除失败", e);
  72.         }
  73.     }
  74.     /**
  75.      * 查看文件信息
  76.      * @param groupName      卷名
  77.      * @param remoteFileName 完整文件名
  78.      * @return
  79.      */
  80.     public static FileInfo getFile(String groupName, String remoteFileName) {
  81.         try {
  82.             StorageClient storageClient = getStorageClient();
  83.             //获取文件方法
  84.             FileInfo file_info = storageClient.get_file_info(groupName, remoteFileName);
  85.             return file_info;
  86.         } catch (Exception e) {
  87.             logger.error("查看文件信息失败", e);
  88.         }
  89.         return null;
  90.     }    /**
  91.      * 获取文件路径,上传至后返回的完整路径
  92.      * @return
  93.      * @throws IOException
  94.      */
  95.     public static String getTrackerUrl() throws IOException {
  96.         return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":8888/";
  97.     }}
复制代码
11.8.前端测试

我们写一个contrller测试即可,service我们不写直接跳过,正常情况下应该要写service,service调我们工具类方法,contrller再调service,我们这里只是演示!!!
controller
  1. package com.zhang.fastdfsdemo.controller;
  2. import com.zhang.fastdfsdemo.pojo.FastDFSFile;
  3. import com.zhang.fastdfsdemo.utils.FastDFSClient;
  4. import org.csource.common.MyException;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. import org.springframework.stereotype.Controller;
  8. import org.springframework.web.bind.annotation.GetMapping;
  9. import org.springframework.web.bind.annotation.PostMapping;
  10. import org.springframework.web.bind.annotation.RequestParam;
  11. import org.springframework.web.multipart.MultipartFile;
  12. import org.springframework.web.servlet.mvc.support.RedirectAttributes;
  13. import java.io.IOException;
  14. import java.io.InputStream;
  15. /**
  16. * @author zb
  17. * @version 1.0
  18. * @date 2023/3/9 21:12
  19. * @ClassName FileContrller
  20. * @Description TODD 测试文件上传、下载、删除、查看
  21. */
  22. @Controller
  23. public class FileContrller {
  24.     private static Logger logger = LoggerFactory.getLogger(FileContrller.class);
  25.     /**
  26.      * 上传文件
  27.      * 我们现在入参是 MultipartFile ,但我们真正要传进去的fastdfs file,那我们可以把它提取出来调用
  28.      *
  29.      * @param file
  30.      * @param redirectAttributes
  31.      * @return
  32.      */
  33.     @PostMapping("upload")
  34.     public String singFile(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes) {
  35.         if (file.isEmpty()) {//判断上传文件是否为空
  36.             redirectAttributes.addFlashAttribute("message", "请选择一个文件上传");
  37.             return "redirect:uploadStatus";
  38.         }
  39.         //拿到客户端
  40.         try {
  41.             //上传文件,拿到返回文件路径
  42.             String path = saveFile(file);
  43.             redirectAttributes.addFlashAttribute("message", "上传成功" + file.getOriginalFilename());
  44.             redirectAttributes.addFlashAttribute("path", "路径:" + path);
  45.         } catch (IOException | MyException e) {
  46.             e.printStackTrace();
  47.         }
  48.         return "redirect:uploadStatus";
  49.     }
  50.     /**
  51.      * 跳转上传文件状态也
  52.      *
  53.      * @return
  54.      */
  55.     @GetMapping("/uploadStatus")
  56.     public String uploadStatus() {
  57.         return "uploadStatus";
  58.     }
  59.     /**
  60.      * 跳转文件上传页
  61.      *
  62.      * @return
  63.      */
  64.     @GetMapping("/")
  65.     public String upload() {
  66.         return "upload";
  67.     }
  68.     /**
  69.      * MultipartFile转fastdfsfile
  70.      *
  71.      * @param multipartFile
  72.      * @return
  73.      * @throws IOException
  74.      */
  75.     public String saveFile(MultipartFile multipartFile) throws IOException, MyException {
  76.         String[] fileAbsolutePath = {};
  77.         String fileName = multipartFile.getOriginalFilename();//获取文件名
  78.         String ext = fileName.substring(fileName.lastIndexOf(".") + 1);//截取后缀
  79.         byte[] file_buff = null;
  80.         InputStream inputStream = multipartFile.getInputStream();
  81.         if (inputStream != null) {
  82.             int len1 = inputStream.available();//拿到长度
  83.             file_buff = new byte[len1];
  84.             inputStream.read(file_buff);
  85.         }
  86.         inputStream.close();
  87.         FastDFSFile file = new FastDFSFile(fileName, file_buff, ext);//转成fastdfs文件类型
  88.         //上传,//返回组名和文件名
  89.         fileAbsolutePath = FastDFSClient.upload(file);
  90.         if (fileAbsolutePath == null) {
  91.             logger.error("上传失败");
  92.         }
  93.         String path = FastDFSClient.getTrackerUrl() + fileAbsolutePath[0] + "/" + fileAbsolutePath[1];//返回完整路径
  94.         return path;
  95.     }
  96. }
复制代码
html
上传文件
  1. <!DOCTYPE html>
  2. <html lang="en">
  3.   <head>
  4.     <meta charset="UTF-8">
  5.     <title></title>
  6.   </head>
  7.   <body>
  8.     <h1>Spring Boot file upload example</h1>
  9.     <form method="post" action="/upload" enctype="multipart/form-data">
  10.       <input type="file" name="file"><br><br>
  11.       <input type="submit" value="submit">
  12.     </form>
  13.   </body>
  14. </html>
复制代码
上传状态
  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>Title</title>
  6. </head>
  7. <body>
  8. <h1>SpringBoot - Upload Status</h1>
  9.     <h2 th:text="${message}"></h2>
  10.     <h2 th:text="${path}"></h2>
  11. </body>
  12. </html>
复制代码
测试
上传成功

流览图


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4