百木园-与人分享,
就是让自己快乐。

FastDFS

FastDFS 入门

分布式文件系统

  • 分布式文件系统 (Distributed File System) 是一个软件/软件服务器,这个软件可以用来管理文件。但这个软件所管理的文件通常不是在一个服务器节点上,而是在多个服务器节点上,这些服务器节点通过网络相连构成一个庞大的文件存储服务器集群,这些服务器都用于存储文件资源,通过分布式文件系统来管理这些服务器上的文件。

  • 常见的分布式文件系统有:FastDFS、GFS、HDFS、Lustre 、Ceph 、GridFS 、mogileFS、TFS 等。

分布式文件系统与传统文件系统对比:

image-20220411115654215

image-20220411120607299

传统方式弊端:

  • 如果用户数量多,IO 操作比较多,对磁盘访问压力很大

  • 如果磁盘发生故障,会造成数据丢失

  • 存储容量有限

分布式文件系统优点:

  • 解决单点故障问题,多服务器节点保证文件读写
  • 提供数据备份
  • 提供扩容等

FastDFS 简介

  • FastDFS 是一个开源的轻量级分布式文件系统,为互联网应用量身定做,简单、灵活、高效,采用 C 语言开发,由阿里巴巴开发并开源。

  • FastDFS 对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载、文件删除)等,解决了大容量文件存储的问题,特别适合以文件为载体的在线服务,如相册网站、文档网站、图片网站、视频网站等等。

  • FastDFS 充分考虑了冗余备份、线性扩容等机制,并注重高可用、高性能等指标,使用 FastDFS 很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。

  • FastDFS 代码托管在 github 上:https://github.com/happyfish100/fastdfs

FastDFS 整体架构

  • FastDFS 文件系统由两大部分构成,一个是客户端,一个是服务端

  • 客户端通常指我们的程序,比如我们的 Java 程序去连接 FastDFS、操作 FastDFS,那我们的 Java 程序就是一个客户端,FastDFS 提供专有 API 访问,目前提供了 C、Java 和 PHP 几种编程语言的 API,用来访问 FastDFS 文件系统。

  • 服务端由两个部分构成:一个是跟踪器(tracker),一个是存储节点(storage)

    • 跟踪器(tracker)主要做调度工作,在内存中记录集群中存储节点 storage 的状态信息,是前端 Client 和后端存储节点 storage的枢纽。因为相关信息全部在内存中,Tracker server 的性能非常高,一个较大的集群(比如上百个group)中有3台就足够了。

    • 存储节点(storage)用于存储文件,包括文件和文件属性(meta data)都保存到存储服务器磁盘上,完成文件管理的所有功能:文件存储、文件同步和提供文件访问等。

FastDFS 环境搭建

资源准备

资源下载地址(或百度云盘直取):https://github.com/happyfish100

image-20220411172455965

资源介绍:

  • fastdfs-6.08.tar.gz:FastDFS 的安装包(只能在 Linux 上安装)
  • fastdfs-client-java-1.28.zip:FastDFS 的 Java 客户端源代码
  • fastdfs-nginx-module-1.22.zip:FastDFS-Nginx 的扩展模块
  • libfastcommon-1.0.56.tar.gz:FastDFS 的公共函数库(Linux 上安装)

FastDFS 安装

FastDFS 没有 Windows 版本,不能在 Windows 下使用。

FastDFS 需要安装部署在 Linux 环境下。

  1. 安装 gcc、libevent、libevent-devel 前置依赖

    yum install gcc libevent libevent-devel -y
    
  2. 将 FastDFS 的安装包 fastdfs-6.08.tar.gz 和 FastDFS 的公共函数库 libfastcommon-1.0.56.tar.gz 上传到 /home/soft/ 下

  3. 解压缩公共函数库 tar -zxvf libfastcommon-1.0.56.tar.gz

  4. 进入解压缩后的公共函数库根目录,执行编译命令:./make.sh

  5. 编译完成后,执行安装命令:./make.sh install,以上,公共函数库安装完毕

  6. 同样,按照此步骤,将 FastDFS 先后进行解压,编译,安装!

  7. 至此 FastDFS 安装完成!

    所有编译出来的文件存放在 /usr/bin 目录下(放在 /usr/bin 下的命令,我们可在任何地方使用,不用切目录)

    所有配置文件存放在 /etc/fdfs 目录下,如下:(后面会使用到这些配置文件)

    client.conf
    storage.conf
    storage_ids.conf
    tracker.conf
    
  8. 注意:还需要将 /home/soft/fastdfs-6.08/conf/下的 http.conf 和 mime.types 两个文件拷贝到 /etc/fdfs/ 目录下;

    后期程序会使用到这两个文件,避免报错;

    cp /home/soft/fastdfs-6.08/conf/http.conf /etc/fdfs/
    cp /home/soft/fastdfs-6.08/conf/mime.types /etc/fdfs/
    

FastDFS 配置

  1. 如果你使用的安装版本中/etc/fdfs下的配置文件有类似 .sample 扩展名,则其为示例配置文件,需要去掉 .sample 后缀才能生效,这点需要注意下。

  2. 将 storage.conf 和 tracker.conf 下载到本机,进行相关配置

  3. 配置 tracker.conf 文件:(跟踪器配置文件)

    找到 base_path 参数,配置 tracker 存储数据的目录并手动创建:(需要确保该目录存在,目录需要我们手动创建好)

    base_path:配置 tracker 存储数据的目录

    base_path = /opt/fastdfs/tracker
    
  4. 配置 storage.conf 文件:(存储节点配置文件)

    找到 base_path 参数,配置 storage 存储数据的目录并手动创建:(需要确保该目录存在,目录需要我们手动创建好)

    base_path:配置 storage 存储数据的目录

    path_path = /opt/fastdfs/storage
    

    找到 store_path0 参数,配置 storage 的磁盘存放路径,即真正存放文件的目录:(需确保该目录存在,需我们手动创建好)

    store_path0:真正存放文件的目录

    store_path0 = /opt/fastdfs/storage/files
    

    找到 tracker_server 参数,配置当前存储节点的跟踪器地址:(只需要配置 IP 即可,端口不要改动)

    tracker_server:注册当前存储节点的跟踪器地址

    tracker_server = 192.168.154.129:22122
    
  5. 将在 Windows 中配置完成并保存的 tracker.conf 和 storage.conf 两个文件上传到 /etc/fdfs目录下,并覆盖原文件;

    上传并覆盖的命令:rz -y

FastDFS 启动

FastDFS 服务启动需要启动两个脚本:tracker 服务和 storage 服务

命令语法参考:
fdfs_trackerd <config_file> [start | stop | restart]
fdfs_storaged <config_file> [start | stop | restart]
  • 确保需要手动创建的目录存在的情况下,分别执行下列命令启动 FastDFS:(不加 start 默认是启动)
fdfs_trackerd /etc/fdfs/tracker.conf 
fdfs_storaged /etc/fdfs/storage.conf
ps -ef|grep fdfs 查看相关进程,是否启动成功

FastDFS 重启

  • 分别执行下列命令重启 FastDFS:
fdfs_trackerd /etc/fdfs/tracker.conf restart
fdfs_storaged /etc/fdfs/storage.conf restart
ps -ef|grep fdfs 查看相关进程,是否重启成功

FastDFS 关闭

  • 分别执行下列命令关闭 FastDFS:(其实也可使用 kill 关闭,但为了防止上传文件中数据丢失,不建议使用 kill 方式)
fdfs_trackerd /etc/fdfs/tracker.conf stop
fdfs_storaged /etc/fdfs/storage.conf stop
ps -ef|grep fdfs 查看相关进程,是否成功关闭

FastDFS 测试

此处示例的上传下载操作其实开发中用不到,主要是感受下 FastDFS 的执行;重点是上传成功后组名和远程文件名的解读!

测试说明:测试文件上传和删除

测试前配置修改

  • 需要修改 /etc/fdfs/client.conf 配置文件的两个参数(自行修改,步骤不再复述;需确保所填的 client 目录存在

    修改小技巧:直接在该文件目录下打开 xftp 传输,直接在传输页面打开文件,修改并保存!

    注意:client.conf 文件只在测试的时候才会用到!

    base_path = /opt/fastdfs/client
    tracker_server = 192.168.154.129:22122
    

测试文件上传

测试命令语法参考:
Usage: fdfs_test <config_file> <operation>
	operation: upload, download, getmeta, setmeta, delete and query_servers
  1. 启动或重启 FastDFS 服务:

    fdfs_trackerd /etc/fdfs/tracker.conf restart
    fdfs_storaged /etc/fdfs/storage.conf restart
    ps -ef|grep fdfs 查看相关进程,是否重启成功
    
  2. 在 /home 下创建一个用于测试的文件 vim test.txt

  3. 上传该文件,执行:

    fdfs_test /etc/fdfs/client.conf upload /home/test.txt
    
  4. 上传成功后会显示一些相关信息,其中最重要的有如下两个信息:【重点!】

    group_name:组名(决定文件存到哪个机器)

    remote_filename:远程文件名(决定文件存放到哪个磁盘目录下)

    FastDFS 存储文件时会自动将文件重命名

    该文件存储的路径:/opt/fastdfs/storage/files/data/00/00/wKiagGJUGnWAGvVNAAAAFU1LnYY803.txt

    group_name=group1, remote_filename=M00/00/00/wKiagGJUGnWAGvVNAAAAFU1LnYY803.txt
    

image-20220411203101646

测试文件下载

下载命令格式:
Usage: fdfs_test <config_file> download <group_name> <remote_filename>
  1. 在 /home 目录下执行下列命令:

    fdfs_test /etc/fdfs/client.conf download group1 M00/00/00/wKiagGJUGnWAGvVNAAAAFU1LnYY803.txt
    
  2. 在 /home 当前目录下看到下载成功的 wKiagGJUGnWAGvVNAAAAFU1LnYY803.txt 文件

测试文件删除

删除命令格式:
Usage: fdfs_test <config_file> delete <group_name> <remote_filename>
  1. 直接执行:

    fdfs_test /etc/fdfs/client.conf delete group1 M00/00/00/wKiagGJUGnWAGvVNAAAAFU1LnYY803.txt
    
  2. 可在 /opt/fastdfs/storage/files/data/00/00路径下看到对应的原文件已经被删除

注意

  • 没有搭建集群默认只有一个组 group1
  • FastDFS 存储文件的同时会存储一个与其对应的属性文件(备份文件也是,一个备份文件对应一个属性文件)
  • 后缀名包含 -m 的为属性文件(meta)
  • 在Linux中并没有磁盘一说,是虚拟的

分布式文件系统 FastDFS 的 HTTP 访问

  • 在文件上传的时候,上传成功的信息中有提示我们可以通过某个路径去访问上传的文件,但是我们直接访问这个路径,却不可以,那么已经上传到 FastDFS 文件系统中的文件,我们如何在浏览器中访问呢?FastDFS 提供了一个 Nginx 扩展模块,利用该模块,我们可以通过 Nginx 访问已经上传到 FastDFS 上的文件。

步骤示例:

1.前期准备

  1. 将 FastDFS-Nginx 的扩展模块 fastdfs-nginx-module-1.22.zip 上传到 /home/soft 下;
  2. 解压:unzip fastdfs-nginx-module-1.22.zip
  3. 将解压缩的 /home/soft/fastdfs-nginx-module-1.22/src/mod_fastdfs.conf 文件下载到 Windows 中,后边会进行配置使用;
  4. 将解压缩的扩展模块源代码的 src 路径 /home/soft/fastdfs-nginx-module-1.22/src拷贝下来,先存着,后面配置重新安装的 Nginx 时会用到这个路径;

2.重装 Nginx

注意:Nginx 的安装需要 Linux 安装相关的几个库,否则编译会出现错误;

如果之前没有安装过 Nginx,则先执行下列命令安装!如果之前安装过 Nginx,下面这相关库的安装步骤就可直接跳过!

yum install gcc openssl openssl-devel pcre pcre-devel zlib zlib-devel –y
  1. 接下来重新安装 Nginx:因为扩展模块必须在 Nginx 的安装过程中才能添加,所以我们需要重新安装一个 nginx,为了和原来已安装的 Nginx 进行区分,我们把新安装的 Nginx 取名为 nginx_fdfs(指定 nginx 安装路径时进行取名)

  2. 将 Nginx 压缩包上传到 /home/soft/ 目录下,并进行解压缩;

  3. 进入解压缩后的 Nginx 目录下,按照下面的说明检查路径,并执行下列配置命令:

    ./configure --prefix=/usr/local/nginx_fdfs --add-module=/home/soft/fastdfs-nginx-module-1.22/src
    	--prefix 是指定nginx安装路径
    	--add-module 指定fastDFS的nginx模块(即扩展模块)的源代码路径(之前已让拷贝存着了)
    
  4. 执行编译命令:make

  5. 执行安装命令make install,完成 Nginx 的重新安装

3.FastDFS 的 Nginx 访问配置

  1. 对之前下载下来的扩展模块 src 下的 mod_fastdfs.conf 文件进行如下四项配置:(nginx_mod 目录需要手动创建)

    创建目录可使用:mkdir -p /opt/fastdfs/nginx_mod命令

    base_path=/opt/fastdfs/nginx_mod
    tracker_server=192.168.154.129:22122
    url_have_group_name = true
    store_path0=/opt/fastdfs/storage/files
    
  2. 使用rz -y命令将配置后的 mod_fastdfs.conf 文件上传到 /etc/fdfs目录下

  3. /usr/local/nginx_fdfs/conf目录下,使用 sz nginx.conf命令,将 nginx.conf 文件下载到 Windows 中,进行如下配置:(注意:是在 server 下新建一个 location 进行配置,千万不要直接在原有的 location 中进行修改!)

    下面正则使用固定,可直接复制!

    #拦截请求路径中包含 /group[1-9]/M0[0-9] 的请求,用 fastdfs的Nginx 模块进行转发
    location ~ /group[1-9]/M0[0-9] {	
         ngx_fastdfs_module;  
    }
    
  4. /usr/local/nginx_fdfs/conf路径下输入 rz -y上传配置好的 nginx.conf 进行原文件覆盖;

  5. 检查配置文件是否有语法错误后,启动 Nginx

    注意:检查语法错误这步骤可以省略,这里复习一下

    /usr/local/nginx_fdfs/sbin/nginx -c /usr/local/nginx_fdfs/conf/nginx.conf -t
    /usr/local/nginx_fdfs/sbin/nginx -c /usr/local/nginx_fdfs/conf/nginx.conf
    
  6. 查看是否启动成功;

    如果 master 和 worker 进程都启动成功,则启动成功;

    若上面两个缺少一个,则有可能是之前的配置出错!可能是 nginx_mod 目录没有创建,或 nginx.conf 中配置信息有问题!

    ps -ef|grep nginx
    
  7. 启动成功后,则可通过 Nginx 访问已经上传到 FastDFS 上的文件。

4.浏览器访问测试

  1. 再进行一次文件上传:在 /home/路径下创建一个文件,如 t.text,随便写进去一些内容;

    上传到 FastDFS 中,执行:

    fdfs_test /etc/fdfs/client.conf upload t.text 
    
  2. 复制控制台中返回的示例文件地址,进行访问测试即可(如果访问时直接是下载文件了,而不是显示文件内容;解决这个问题,可参考https://blog.csdn.net/qq_21457601/article/details/51853248?utm_source=blogxgwz3;其实问题不大,目的已经达到了,即使是直接下载了文件,访问测试也是成功的)

    http://192.168.154.129/group1/M00/00/00/wKiagGJURUuAMfHuAAAAHUAYZwU38.text
    

访问流程

访问流程/扩展模块执行流程图:

image-20220411233903328

FastDFS 在普通 Java 项目中开发示例

在实际项目开发,FastDFS 提供的主要功能

  • upload:上传文件
  • download:下载文件
  • delete:删除文件

FastDFS 文件系统的 Java 客户端

  • FastDFS 文件系统 Java 客户端是指采用 Java 语言编写的一套程序,专门用来访问 fastDFS 文件系统,其实就是一个 jar 包。
  • 它并没有添加到 maven 的中央仓库中,我们要使用,需要手动下载源代码,编译到自己的本地 maven 仓库。【现在中央仓库好像有了?自己看着选择吧,可以用自己编译的,也可以直接使用中央仓库的
  • 下载地址:https://github.com/happyfish100/fastdfs(其实之前准备资源,已经让下载过)

将 FastDFS 文件系统 Java 客户端编译到本地 maven 仓库步骤:

  1. 下载 FastDFS 客户端源代码 fastdfs-client-java-1.28.zip,解压缩;

  2. 进入解压缩的根目录,在文件路径地址栏输入 cmd 命令,进入 dos 窗口,输入 mvn clean install命令,即可将其编译到自己的本地 maven 仓库中,在项目中配置其依赖后可直接使用。

    [INFO] Installing C:\\Users\\linwe\\Downloads\\fastdfs-client-java-1.28\\pom.xml to D:\\Java\\maven\\repository\\org\\csource\\fastdfs-client-java\\1.28-SNAPSHOT\\fastdfs-client-java-1.28-SNAPSHOT.pom
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    

注意:当在项目 pom 中引入该 maven 依赖时,若无法识别出来,则可以在 IDEA 中设置更新下本地仓库。

image-20220412091139752

Java 项目中使用 FastDFS(用的少,一般在web项目中用)

需求:在普通 Java 项目中使用 FastDFS 文件系统实现对文件的上传,下载和删除操作

步骤示例:

  1. 创建普通 maven java 项目,不需要使用 archetype 原型模板

  2. 在 pom 中添加 FastDFS 客户端依赖,并刷新 pom(可用自己编译的,也可在中央仓库中自己找)

    <dependencies>
        <!--中央仓库中的FastDFS依赖-->
        <!--<dependency>-->
        <!--    <groupId>com.github.tobato</groupId>-->
        <!--    <artifactId>fastdfs-client</artifactId>-->
        <!--    <version>1.27.2</version>-->
        <!--</dependency>-->
    
        <!--自己手动编译的本地仓库中FastDFS依赖-->
        <dependency>
            <groupId>org.csource</groupId>
            <artifactId>fastdfs-client-java</artifactId>
            <version>1.28-SNAPSHOT</version>
        </dependency>
    </dependencies>
    
  3. 在 resources 下创建一个 fastdfs.conf 文件,配置 tracker 跟踪器服务,配置自己的 IP

    tracker_server=192.168.154.129:22122
    
  4. 此处直接创建并将相关方法封装为工具类,改参数进行测试调用

    public class FastDFSUtil {
    
        public static void main(String[] args) {
            upload();
            // download();
            // delete();
        }
    
        /**
         * 文件上传
         */
        public static void upload() {
    
            try {
                //读取FastDFS配置文件,用于将所有的tracker的地址读取到内存中
                ClientGlobal.init(\"fastdfs.conf\");
                // 使用无参构造方式创建tracker客户端对象(配合配置文件使用),用于获取tracker和storage的服务端对象
                TrackerClient tc = new TrackerClient();
    
                // 通过tracker客户端对象获取tracker和storage的服务端对象
                TrackerServer ts = tc.getTrackerServer();
                StorageServer ss = tc.getStoreStorage(ts);
    
                // 通过tracker和storage的服务端对象创建Storage客户端对象,来操作数据
                StorageClient sc = new StorageClient(ts, ss);
                /**
                 * 文件上传
                 *  参数1:需上传文件的绝对路径
                 *  参数2:文件扩展名
                 *  参数3:文件的属性文件(通常不用上传)
                 *  返回值:返回一个String数组,此数据非常重要,必须妥善管理,建议存入数据库
                 *      数组中第一个元素为文件所在组名
                 *      数组中第二个元素为文件所在远程路径名
                 */
                String[] result = sc.upload_file(\"D:/image/love.png\", \"png\", null);
    
                for (String str : result) {
                    System.out.println(str);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (MyException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 文件下载
         */
        public static void download() {
    
            try {
                //读取FastDFS配置文件,用于将所有的tracker的地址读取到内存中
                ClientGlobal.init(\"fastdfs.conf\");
                // 使用无参构造方式创建tracker客户端对象(配合配置文件使用),用于获取tracker和storage的服务端对象
                TrackerClient tc = new TrackerClient();
    
                // 通过tracker客户端对象获取tracker和storage的服务端对象
                TrackerServer ts = tc.getTrackerServer();
                StorageServer ss = tc.getStoreStorage(ts);
    
                // 通过tracker和storage的服务端对象创建Storage客户端对象,来操作数据
                StorageClient sc = new StorageClient(ts, ss);
                /**
                 * 文件下载
                 *  参数1:需下载文件的组名
                 *  参数2:需下载文件的远程文件名
                 *  参数3:需要保存的本地文件名(绝对路径+文件名)
                 *  返回值:返回0则下载成功;其他值,则下载失败
                 */
                String groupName = \"group1\";
                String remoteFileName = \"M00/00/00/wKiagWJU6gyEWTbdAAAAAD_4r6Y944.png\";
                String localFileName = \"D:/temp/a.png\";
                int ret = sc.download_file(groupName, remoteFileName, localFileName);
                System.out.println(\"download========> \" + ret);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (MyException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 文件删除
         */
        p static void delete() {
    
            try {
                //读取FastDFS配置文件,用于将所有的tracker的地址读取到内存中
                ClientGlobal.init(\"fastdfs.conf\");
                // 使用无参构造方式创建tracker客户端对象(配合配置文件使用),用于获取tracker和storage的服务端对象
                TrackerClient tc = new TrackerClient();
    
                // 通过tracker客户端对象获取tracker和storage的服务端对象
                TrackerServer ts = tc.getTrackerServer();
                StorageServer ss = tc.getStoreStorage(ts);
    
                // 通过tracker和storage的服务端对象创建Storage客户端对象,来操作数据
                StorageClient sc = new StorageClient(ts, ss);
                /**
                 * 文件删除
                 *  参数1:需删除文件的组名
                 *  参数2:需删除文件的远程文件名
                 *  返回值:返回0则删除成功;其他值,则删除失败
                 */
                String groupName = \"group1\";
                String remoteFileName = \"M00/00/00/wKiagWJU6gyEWTbdAAAAAD_4r6Y944.png\";
                int ret = sc.delete_file(groupName, remoteFileName);
                System.out.println(\"delete========> \" + ret);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (MyException e) {
                e.printStackTrace();
            }
        }
    }
    
  5. 在浏览器中输入访问地址进行资源访问测试的时候注意下地址中资源拼接方式:

    上传成功返回String数组中两个参数:
    group1
    M00/00/00/wKiagWJU-t-EJfOFAAAAAD_4r6Y931.png
    
    则访问地址格式为:ip/group1/M00/00/00/wKiagWJU-t-EJfOFAAAAAD_4r6Y931.png
    
    即:
    192.168.154.129/group1/M00/00/00/wKiagWJU-t-EJfOFAAAAAD_4r6Y931.png
    

FastDFS 在 web 项目中开发示例

需求:对项目进行管理,在 WEB 项目中实现对文件的上传下载和删除操作。

流程分析:将文件从本地上传到 tomcat 中,在 tomcat 中拿到文件流,将文件流传到 FastDFS 中,并将上传返回的数据存入数据库中。

步骤:

1.数据库环境搭建

  1. 创建 fastdfs 数据库

  2. 新建查询,执行下列命令,创建 creditor_info 表

    示例表中:只有 id、group_name、remote_file_path、old_file_name、file_size、file_type 这几个是重要字段!

    -- ----------------------------
    -- Table structure for creditor_info
    -- ----------------------------
    DROP TABLE IF EXISTS `creditor_info`;
    CREATE TABLE `creditor_info` (
      `id` int(11) NOT NULL AUTO_INCREMENT COMMENT \'主键\',
      `real_name` varchar(35) DEFAULT NULL COMMENT \'债权借款人姓名\',
      `id_card` varchar(18) DEFAULT NULL COMMENT \'债权借款人身份证\',
      `address` varchar(150) DEFAULT NULL COMMENT \'债权借款人地址\',
      `sex` int(1) DEFAULT NULL COMMENT \'1男2女\',
      `phone` varchar(11) DEFAULT NULL COMMENT \'债权借款人电话\',
      `money` decimal(10,2) DEFAULT NULL COMMENT \'债权借款人借款金额\',
      `group_name` varchar(10) DEFAULT NULL COMMENT \'债权合同所在组\',
      `remote_file_path` varchar(150) DEFAULT NULL COMMENT \'债权合同所在路径\',
      `old_file_name` varchar(255) DEFAULT NULL COMMENT \'文件上传前的名字,用于下载文件时指定默认文件名使用\',
      `file_size` bigint(20) DEFAULT NULL COMMENT \'文件大小,用于下载文件啊时提供下载进度\',
      `file_type` varchar(255) DEFAULT NULL COMMENT \'文件类型,用于显示与文件类型相同的图标,作用不是很大\',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
    
    -- ----------------------------
    -- Records of creditor_info
    -- ----------------------------
    INSERT INTO `creditor_info` VALUES (\'1\', \'张三\', \'123412341\', \'北京\', \'1\', \'23412341\', \'12433.00\', \'1\', \'1\', \'1\', \'1\', \'1\');
    INSERT INTO `creditor_info` VALUES (\'2\', \'李四\', \'132414123\', \'上海\', \'0\', \'12344312\', \'21343.00\', null, null, null, null, null);
    
  3. 此处用的是 Linux 中的 mysql:用户名 root,密码 123

2.Web 项目环境搭建

  1. 创建 Spring Boot 项目,选择 Spring Boot DevTools(热部署工具)、Spring Web、Thymeleaf、MySQL、MyBatis 依赖;

    注意:如果配置文件中使用的是低版本的 mysql 驱动配置,pom 中的 mysql 需要添加下低版本的版本号对应;

  2. 配置 application.properties

    # 配置数据库连接信息 注意mysql新旧版本配置不同(如果用的低版本的驱动,pom里需要将mysql也指定下使用低版本的版本号)
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://192.168.154.129:3306/fastdfs?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    spring.datasource.username=root
    spring.datasource.password=123
    
    # 开发阶段,关闭thymeleaf的缓存,不然无法看到实时页面
    spring.thymeleaf.cache=false
    # 去掉html5的语法验证(thymeleaf对html的标签约束非常严格,所有的标签必须有开有闭,比如<br></br>或者<br/>是可以的,但是<br>会报错,
    # 配置spring.thymeleaf.mode=LEGACYHTML5 目的就是为了解决这个问题,可以使页面松校验。)
    spring.thymeleaf.mode=LEGACYHTML5
    
    #配置mapper文件路径
    mybatis.mapper-locations=classpath:mapper/*.xml
    #开启日志
    #mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    
    #文件上传大小参考配置
    #设置SpringMVC允许上传的单个文件大小,默认为1MB
    #spring.servlet.multipart.max-file-size=1MB
    #设置SpringMVC表单请求中允许上传文件的总大小,默认为10MB
    #spring.servlet.multipart.max-request-size=10MB
    
  3. 在 templates 下创建 creditors.html 测试页面

    <!DOCTYPE html>
    <html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\">
    <head>
        <meta charset=\"UTF-8\">
        <title>Title</title>
    </head>
    <body>
        1111111111111111111
    </body>
    </html>
    
  4. 创建控制器类

    @Controller
    public class CreditorInfoController {
    
        @RequestMapping(\"/\")
        public String creditors() {
            return \"creditors\";
        }
    }
    
  5. 运行主启动类,访问测试 localhost:8080/

3.使用mybatis生成器(逆向工程生成代码)

PS:使用 mybatis 自动代码生成器,可自动生成 model 实体类, dao 接口以及 mapper 映射文件。

此处使用 mapper 文件和 dao 接口分开管理的方式。

  1. 在 pom 的 plugins 标签下添加 mybatis 自动代码生成插件

    <!--mybatis自动代码生成插件-->
    <plugin>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>1.3.6</version>
        <configuration>
            <!--配置文件的位置:在项目的根目录下,和src平级-->
            <configurationFile>GeneratorMapper.xml</configurationFile>
            <verbose>true</verbose>
            <overwrite>true</overwrite>
        </configuration>
    </plugin>
    
  2. 在 pom 的 build 标签下添加资源插件,处理资源文件

    <!--   处理资源文件     -->
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/**.*</include>
            </includes>
        </resource>
    </resources>
    
  3. 在项目根目录下创建 GeneratorMapper.xml文件,其与 src 平级;将下列内容拷贝到文件中;依照注释修改配置,修改完后,点开右侧 maven 快捷窗口,使用当前项目下 plugins 下 mybatis-genetate 下的 mybatis 自动代码生成插件进行生成。

    <?xml version=\"1.0\" encoding=\"UTF-8\"?>
    <!DOCTYPE generatorConfiguration
            PUBLIC \"-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN\"
            \"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd\">
    
    <generatorConfiguration>
    
        <!-- 指定连接数据库的JDBC驱动包所在位置,指定到你本机的完整路径 -->
        <classPathEntry location=\"D:\\Java\\tools\\mysql-connector-java-8.0.28.jar\"/>
    
        <!-- 配置table表信息内容体,targetRuntime指定采用MyBatis3的版本 -->
        <context id=\"tables\" targetRuntime=\"MyBatis3\">
    
            <!-- 抑制生成注释,由于生成的注释都是英文的,可以不让它生成 -->
            <commentGenerator>
                <property name=\"suppressAllComments\" value=\"true\" />
            </commentGenerator>
    
            <!-- 配置数据库连接信息 -->
            <jdbcConnection driverClass=\"com.mysql.cj.jdbc.Driver\"
                            connectionURL=\"jdbc:mysql://192.168.154.129:3306/fastdfs?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=GMT%2B8\"
                            userId=\"root\"
                            password=\"123\">
                <!--MySQL 不支持 schema 或者 catalog 所以需要添加这个-->
                <!--设置原因参考:https://blog.csdn.net/qq_40233736/article/details/83314596-->
                <property name=\"nullCatalogMeansCurrent\" value=\"true\" />
            </jdbcConnection>
    
            <!-- 生成model类,targetPackage指定model类的包名, targetProject指定生成的model放在eclipse的哪个工程下面-->
            <javaModelGenerator targetPackage=\"com.luis.model\"
                                targetProject=\"D:\\1a-Projects\\test-projects\\fastdfs\\springboot-web-fastdfs\\src\\main\\java\">
                <property name=\"enableSubPackages\" value=\"false\" />
                <property name=\"trimStrings\" value=\"false\" />
            </javaModelGenerator>
    
            <!-- 生成MyBatis的Mapper.xml文件,targetPackage指定mapper.xml文件的包名, targetProject指定生成的mapper.xml放在eclipse的哪个工程下面 -->
            <sqlMapGenerator targetPackage=\"mapper\" targetProject=\"src/main/resources\">
                <property name=\"enableSubPackages\" value=\"false\" />
            </sqlMapGenerator>
    
            <!-- 生成MyBatis的Mapper接口类文件,targetPackage指定Mapper接口类的包名, targetProject指定生成的Mapper接口放在eclipse的哪个工程下面 -->
            <javaClientGenerator type=\"XMLMAPPER\" targetPackage=\"com.luis.dao\" targetProject=\"src/main/java\">
                <property name=\"enableSubPackages\" value=\"false\" />
            </javaClientGenerator>
    
            <!-- 数据库表名及对应的Java模型类名 -->
            <table tableName=\"creditor_info\" domainObjectName=\"CreditorInfo\"
                   enableCountByExample=\"false\"
                   enableUpdateByExample=\"false\"
                   enableDeleteByExample=\"false\"
                   enableSelectByExample=\"false\"
                   selectByExampleQueryId=\"false\"/>
    
            <table tableName=\"b_bid_info\" domainObjectName=\"BidInfo\"
                   enableCountByExample=\"false\"
                   enableUpdateByExample=\"false\"
                   enableDeleteByExample=\"false\"
                   enableSelectByExample=\"false\"
                   selectByExampleQueryId=\"false\"/>
    
    
            <table tableName=\"b_income_record\" domainObjectName=\"IncomeRecord\"
                   enableCountByExample=\"false\"
                   enableUpdateByExample=\"false\"
                   enableDeleteByExample=\"false\"
                   enableSelectByExample=\"false\"
                   selectByExampleQueryId=\"false\"/>
    
    
            <table tableName=\"b_recharge_record\" domainObjectName=\"RechargeRecord\"
                   enableCountByExample=\"false\"
                   enableUpdateByExample=\"false\"
                   enableDeleteByExample=\"false\"
                   enableSelectByExample=\"false\"
                   selectByExampleQueryId=\"false\"/>
    
    
            <table tableName=\"u_user\" domainObjectName=\"User\"
                   enableCountByExample=\"false\"
                   enableUpdateByExample=\"false\"
                   enableDeleteByExample=\"false\"
                   enableSelectByExample=\"false\"
                   selectByExampleQueryId=\"false\"/>
    
    
            <table tableName=\"u_finance_account\" domainObjectName=\"FinanceAccount\"
                   enableCountByExample=\"false\"
                   enableUpdateByExample=\"false\"
                   enableDeleteByExample=\"false\"
                   enableSelectByExample=\"false\"
                   selectByExampleQueryId=\"false\"/>
        </context>
    
    </generatorConfiguration>
    
  4. application.properties 中添加 mybatis 相关配置

    #配置mapper文件路径
    mybatis.mapper-locations=classpath:mapper/*.xml
    #开启日志
    mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    
  5. 在主启动类上添加包扫描注解@MapperScan,扫描 dao 所在的包(防止后面忘记了,先加上)

    作用:扫描 dao 下的所有 mapper 类作为 Mapper 映射文件。

    @SpringBootApplication
    @MapperScan(basePackages = \"com.luis.dao\")
    public class SpringbootWebFastdfsApplication {
        public static void main(String[] args) {
            SpringApplication.run(SpringbootWebFastdfsApplication.class, args);
        }
    }
    

4.显示数据列表

  1. 控制器类补充查询功能

    @Controller
    public class CreditorInfoController {
    
        @Resource
        private CreditorInfoServer creditorInfoServer;
    
        @RequestMapping(\"/\")
        public String creditors(Model model) {
            List<CreditorInfo> creditorInfoList = creditorInfoServer.queryAll();
            model.addAttribute(\"creditorInfoList\", creditorInfoList);
            return \"creditors\";
        }
    }
    
  2. 补充 service 接口和实现类

    public interface CreditorInfoServer {
        List<CreditorInfo> queryAll();
    }
    
    @Service
    public class CreditorInfoServiceImpl implements CreditorInfoServer {
    
        @Resource
        private CreditorInfoMapper creditorInfoMapper;
    
        @Override
        public List<CreditorInfo> queryAll() {
            List<CreditorInfo> list = creditorInfoMapper.selectAll();
            return list;
        }
    }
    
  3. dao 中添加全查方法

    public interface CreditorInfoMapper {
        int deleteByPrimaryKey(Integer id);
    
        int insert(CreditorInfo record);
    
        int insertSelective(CreditorInfo record);
    
        CreditorInfo selectByPrimaryKey(Integer id);
    
        int updateByPrimaryKeySelective(CreditorInfo record);
    
        int updateByPrimaryKey(CreditorInfo record);
        
        // 新增的全查方法
        List<CreditorInfo> selectAll();
    }
    
  4. mapper 中补充全查的 SQL

    <select id=\"selectAll\" resultMap=\"BaseResultMap\">
        select
        <include refid=\"Base_Column_List\" />
        from creditor_info
    </select>
    
  5. 完善显示数据的页面

    <!DOCTYPE html>
    <html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\">
    <head>
        <meta charset=\"UTF-8\">
        <title>test</title>
    </head>
    <body>
        <table>
            <tr>
                <th>序号</th>
                <th>姓名</th>
                <th>性别</th>
                <th>电话</th>
                <th>地址</th>
                <th></th>
            </tr>
            <tr th:each=\"creditorInfo:${creditorInfoList}\">
                <td th:text=\"${creditorInfo.id}\">序号</td>
                <td th:text=\"${creditorInfo.realName}\">姓名</td>
                <td th:text=\"${creditorInfo.sex == 1?\'男\':\'女\'}\">性别</td>
                <td th:text=\"${creditorInfo.phone}\">电话</th>
                <td th:text=\"${creditorInfo.address}\">地址</td>
                <td>
                    <span th:if=\"${creditorInfo.remoteFilePath == null || creditorInfo.remoteFilePath == \'\'}\">
                        <a th:href=\"@{|/upload/${creditorInfo.id}|}\">上传</a>
                    </span>
                    <span th:if=\"${creditorInfo.remoteFilePath != null && creditorInfo.remoteFilePath != \'\'}\">
                        <a th:href=\"@{|/download|}\">下载</a>
                        <a th:href=\"@{|/delete|}\">删除</a>
                    </span>
                </td>
            </tr>
        </table>
    </body>
    </html>
    
  6. 运行主启动类,测试

image-20220412174502775

5.Web 中操作 FastDFS 完成上传

6.Web 中操作 FastDFS 完成下载

7.Web 中操作 FastDFS 完成删除

由于操作不好记录,以下相关实现提供相关实现参考:

相关页面参考:

creditors.html

<!DOCTYPE html>
<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\">
<head>
    <meta charset=\"UTF-8\">
    <title>creditors.html</title>
</head>
<body>
    <table>
        <tr>
            <th>序号</th>
            <th>姓名</th>
            <th>性别</th>
            <th>电话</th>
            <th>地址</th>
            <th></th>
        </tr>
        <tr th:each=\"creditorInfo:${creditorInfoList}\">
            <td th:text=\"${creditorInfo.id}\">序号</td>
            <td th:text=\"${creditorInfo.realName}\">姓名</td>
            <td th:text=\"${creditorInfo.sex == 1?\'男\':\'女\'}\">性别</td>
            <td th:text=\"${creditorInfo.phone}\">电话</th>
            <td th:text=\"${creditorInfo.address}\">地址</td>
            <td>
                <span th:if=\"${creditorInfo.remoteFilePath == null || creditorInfo.remoteFilePath == \'\'}\">
                    <a th:href=\"@{|/upload/${creditorInfo.id}|}\">上传</a>
                </span>
                <span th:if=\"${creditorInfo.remoteFilePath != null && creditorInfo.remoteFilePath != \'\'}\">
                    <a th:href=\"@{|/download/${creditorInfo.id}|}\">下载</a>
                    <a th:href=\"@{|/delete/${creditorInfo.id}|}\">删除</a>
                </span>
            </td>
        </tr>
    </table>
</body>
</html>

upload.html

<!DOCTYPE html>
<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\">
<head>
    <meta charset=\"UTF-8\">
    <title>upload.html</title>
</head>
<body>
    <!--  上传文件的表单,method必须为post,并且必须指定enctype属性为multipart/form-data  -->
    <!--  enctype=\"multipart/form-data\" 简单说明:让表单以二进制的形式传递数据,支持流文件类型  -->
    <form th:action=\"@{|/upload|}\" target=\"curPage\" method=\"post\" enctype=\"multipart/form-data\">
        姓名:<span th:text=\"${creditorInfo.realName}\"></span><br/>
        性别:<span th:text=\"${creditorInfo.sex==1?\'男\':\'女\'}\"></span><br/>
        电话:<span th:text=\"${creditorInfo.phone}\"></span><br/>
        地址:<span th:text=\"${creditorInfo.address}\"></span><br/>
        文件:<input type=\"file\" name=\"myFile\" /><br/>
        <input type=\"hidden\" name=\"id\" th:value=\"${creditorInfo.id}\" /><br/>
        <input type=\"submit\" value=\"上传文件\"><br/>
    </form>

    <!--  嵌入式框架框架  -->
    <iframe name=\"curPage\" style=\"display: none\"></iframe>

</body>
</html>

success.html

<!DOCTYPE html>
<html lang=\"en\">
<head>
    <meta charset=\"UTF-8\">
    <title>Title</title>
  <script type=\"text/javascript\">
      if (confirm(\"[[${msg}]]\")) {
        window.top.location.href = \"[[${url}]]\";
      }
  </script>
</head>
<body>

</body>
</html>

工具类参考:

public class FastDFSUtil {

    private FastDFSUtil() {
    }

    /**
     * 文件上传
     *
     * @param fileBuff    上传文件的字节数组
     * @param fileExtName 上传文件的扩展名
     * @return String数组
     * 数组中第一个元素为文件所在组名
     * 数组中第二个元素为文件所在远程路径名
     */
    public static String[] upload(byte[] fileBuff, String fileExtName) {

        try {
            //读取FastDFS配置文件,用于将所有的tracker的地址读取到内存中
            ClientGlobal.init(\"fastdfs.conf\");
            // 使用无参构造方式创建tracker客户端对象(配合配置文件使用),用于获取tracker和storage的服务端对象
            TrackerClient tc = new TrackerClient();

            // 通过tracker客户端对象获取tracker和storage的服务端对象
            TrackerServer ts = tc.getTrackerServer();
            StorageServer ss = tc.getStoreStorage(ts);

            // 通过tracker和storage的服务端对象创建Storage客户端对象,来操作数据
            StorageClient sc = new StorageClient(ts, ss);
            /**
             * 文件上传
             *  参数1:需上传文件的字节数组
             *  参数2:文件扩展名
             *  参数3:文件的属性文件(通常不用上传)
             *  返回值:返回一个String数组,此数据非常重要,必须妥善管理,建议存入数据库
             *      数组中第一个元素为文件所在组名
             *      数组中第二个元素为文件所在远程路径名
             */
            return sc.upload_file(fileBuff, fileExtName, null);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (MyException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文件下载
     *
     * @param groupName      需下载文件的组名
     * @param remoteFileName 需下载文件的远程文件名
     * @return byte[] 需下载文件的字节数组
     */
    public static byte[] download(String groupName, String remoteFileName) {

        try {
            //读取FastDFS配置文件,用于将所有的tracker的地址读取到内存中
            ClientGlobal.init(\"fastdfs.conf\");
            // 使用无参构造方式创建tracker客户端对象(配合配置文件使用),用于获取tracker和storage的服务端对象
            TrackerClient tc = new TrackerClient();

            // 通过tracker客户端对象获取tracker和storage的服务端对象
            TrackerServer ts = tc.getTrackerServer();
            StorageServer ss = tc.getStoreStorage(ts);

            // 通过tracker和storage的服务端对象创建Storage客户端对象,来操作数据
            StorageClient sc = new StorageClient(ts, ss);

            return sc.download_file(groupName, remoteFileName);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (MyException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文件删除
     * @param groupName 需删除文件的组名
     * @param remoteFileName 需删除文件的远程文件名
     */
    public static void delete(String groupName, String remoteFileName) {

        try {
            //读取FastDFS配置文件,用于将所有的tracker的地址读取到内存中
            ClientGlobal.init(\"fastdfs.conf\");
            // 使用无参构造方式创建tracker客户端对象(配合配置文件使用),用于获取tracker和storage的服务端对象
            TrackerClient tc = new TrackerClient();

            // 通过tracker客户端对象获取tracker和storage的服务端对象
            TrackerServer ts = tc.getTrackerServer();
            StorageServer ss = tc.getStoreStorage(ts);

            // 通过tracker和storage的服务端对象创建Storage客户端对象,来操作数据
            StorageClient sc = new StorageClient(ts, ss);

            sc.delete_file(groupName, remoteFileName);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (MyException e) {
            e.printStackTrace();
        }
    }
}

控制器类参考:

@Controller
public class CreditorInfoController {

    @Resource
    private CreditorInfoServer creditorInfoServer;

    @RequestMapping(\"/\")
    public String creditors(Model model) {
        List<CreditorInfo> creditorInfoList = creditorInfoServer.queryAll();
        model.addAttribute(\"creditorInfoList\", creditorInfoList);
        return \"creditors\";
    }

    @GetMapping(\"/upload/{id}\")
    public String toUpload(@PathVariable Integer id, Model model) {
        CreditorInfo creditorInfo = creditorInfoServer.findById(id);
        model.addAttribute(\"creditorInfo\", creditorInfo);
        return \"upload\";
    }

    /**
     * 文件上传
     *      参数MultipartFile:为Spring提供的一个类,专门用于封装请求中的文件数据,
     *      其属性名必须与表单中文件域的名字完全相同!否则无法获取文件数据!
     */
    @PostMapping(\"/upload\")
    public String upload(Integer id, MultipartFile myFile, Model model) throws IOException {
        // System.out.println(myFile.getBytes()); // 获取文件对应字节数组
        // System.out.println(myFile.getContentType()); // 获取文件类型
        // System.out.println(myFile.getInputStream()); // 获取文件对应的输入流
        // System.out.println(myFile.getName()); // 获取表单元素名
        // System.out.println(myFile.getOriginalFilename()); // 获取文件名
        // System.out.println(myFile.getSize()); // 获取文件大小
        // System.out.println(myFile.isEmpty()); //判断文件是否为空;文件没有上传或文件大小为0,这个值都是true

        // 获取上传文件对应的字节数组
        byte[] fileBuff = myFile.getBytes();
        // 获取上传文件的文件名
        String fileName = myFile.getOriginalFilename();
        // 获取上传文件的扩展名
        // #TODO 可能会出现问题,因为有的文件有可能没有扩展名,所以必要时需要做逻辑控制
        String fileExtName = fileName.substring(fileName.lastIndexOf(\".\") + 1);
        Long fileSize = myFile.getSize(); // 文件大小
        String fileType = myFile.getContentType(); // 文件类型

        // ================进行文件上传========上传到FastDFS文件系统中=============
        String[] result = FastDFSUtil.upload(fileBuff, fileExtName);

        CreditorInfo creditorInfo = new CreditorInfo();
        creditorInfo.setId(id); // id
        creditorInfo.setGroupName(result[0]); // 组名
        creditorInfo.setRemoteFilePath(result[1]); // 远程文件地址
        creditorInfo.setOldFileName(fileName); // 文件名
        creditorInfo.setFileSize(fileSize); // 文件大小
        creditorInfo.setFileType(fileType); // 文件类型

        // ==========更新数据库信息==========
        creditorInfoServer.updateFileInfo(creditorInfo);
        model.addAttribute(\"msg\", \"上传成功,是否返回列表页面?\");
        model.addAttribute(\"url\", \"/\");
        return \"success\";
    }

    /**
     * 文件下载
     * @param id 需要下载的文件主键
     * @return ResponseEntity 表示一个响应的实体,这个类是Spring提供的一个类,是Spring响应数据时的一个对象,
     *          这个对象有包含着响应时的编码如404 202 等,以及响应的头文件信息,以及响应时的具体数据;
     *          这个对象可以是一段html代码,也可是是一段JS,也可以是一段普通字符串,也可以是一个文件的流
     */
    @RequestMapping(\"/download/{id}\")
    public ResponseEntity<byte[]> download(@PathVariable Integer id) {

        // 获取响应数据(即下载后成功返回的字节数组)
        CreditorInfo info = creditorInfoServer.findById(id);
        String groupName = info.getGroupName();
        String remoteFilePath = info.getRemoteFilePath();
        byte[] buffFile = FastDFSUtil.download(groupName, remoteFilePath);

        // 设置响应时的头文件信息
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); // 设置响应类型为文件类型
        headers.setContentLength(info.getFileSize()); // 设置响应时的文件大小,用于提供下载进度显示使用

        // 设置下载时的默认文件名
        headers.setContentDispositionFormData(\"attachment\", info.getOldFileName());
        /**
         * 创建响应实体对象,Spring会将这个对象返回给浏览器,作为响应数据;(我们需要填充数据)
         *      参数1:响应时的具体数据
         *      参数2:响应时的头文件信息
         *      参数3:响应时的状态码
         */
        ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(buffFile, headers, HttpStatus.OK);
        return responseEntity;
    }

    /**
     * 文件删除
     *      文件删除操作选择在业务层进行,因为需要先查数据,才能进行删除,减少方法调用。
     * @param id
     * @return
     */
    @RequestMapping(\"/delete/{id}\")
    public String delete(@PathVariable Integer id) {
        creditorInfoServer.deleteFileById(id);
        return \"redirect:/\";
    }
}

service 接口实现类参考:

@Service
public class CreditorInfoServiceImpl implements CreditorInfoServer {

    @Resource
    private CreditorInfoMapper creditorInfoMapper;

    @Override
    public List<CreditorInfo> queryAll() {
        List<CreditorInfo> list = creditorInfoMapper.selectAll();
        return list;
    }

    @Override
    public CreditorInfo findById(Integer id) {
        CreditorInfo creditorInfo = creditorInfoMapper.selectByPrimaryKey(id);
        return creditorInfo;
    }

    @Override
    public int updateFileInfo(CreditorInfo creditorInfo) {
        int ret = creditorInfoMapper.updateByPrimaryKeySelective(creditorInfo);
        return ret;
    }

    @Override
    public void deleteFileById(Integer id) {
        CreditorInfo creditorInfo = creditorInfoMapper.selectByPrimaryKey(id);
        String groupName = creditorInfo.getGroupName();
        String remoteFilePath = creditorInfo.getRemoteFilePath();

        // 先删除远程文件
        FastDFSUtil.delete(groupName, remoteFilePath);

        // 再修改数据库文件数据
        creditorInfo.setGroupName(\"\");
        creditorInfo.setRemoteFilePath(\"\");
        creditorInfo.setOldFileName(\"\");
        creditorInfo.setFileSize(0L);
        creditorInfo.setFileType(\"\");
        creditorInfoMapper.updateByPrimaryKeySelective(creditorInfo);
    }
}

gitee 上完整示例项目参考:

Web 中操作 FastDFS 完成上传,下载,删除相关步骤,参考下列完整的示例项目:

https://gitee.com/lw2gitee/springboot-web-fastdfs

可克隆下来,在本地查看实现流程以及步骤。

FastDFS 分布式文件系统集群

其实之前做的集群都算是伪集群,因为之前都是在一台机器上搭建的集群服务;

下面示例的 FastDFS 分布式文件系统集群是在七个虚拟机中进行搭建,相当于七台机器,是真正的集群架构。

架构图

image-20220413105559831

  • 同一个组中数据相同,机器之间有数据备份,建议同一个组中所有机器性能和配置要相同
  • 不同组中存的数据不同
  • 如果有4台机器存储数据,每个机器有1G存储空间,那么我们文件的存储上限是2G(因为每台机器对应一个备份机器)

环境搭建步骤

PS:建议如果实际操作,不要直接按照下面步骤来!其中有很多细节需要注意,建议跟着视频操作!

视频链接:https://www.bilibili.com/video/BV1ta4y1v7Kw?p=25

FastDFS分布式文件系统集群环境搭建-操作步骤手册

搭建一个FastDFS分布式文件系统集群,推荐至少部署6个服务器节点;
================================搭建FastDFS的集群==============================
第一步:安装6个迷你版的Linux,迷你版Linux没有图形界面,占用磁盘及资源小,企业里面使用的Linux都是没有图形界面的Linux;

第二步:由于迷你版Linux缺少一些常用的工具库,操作起来不方便,推荐安装如下的工具库:(不要分别安装,一次全安装)
1、安装lrzsz, yum install lrzsz -y
2、安装wget, yum install wget -y
4、安装vim, yum install vim -y
5、安装unzip,yum install unzip -y
6、安装ifconfig,yum install net-tools -y

一次性全部安装:    
yum install lrzsz wget vim unzip net-tools -y

7、安装nginx及fastdfs需要的库依赖:
yum install gcc perl openssl openssl-devel pcre pcre-devel zlib zlib-devel libevent libevent-devel -y

第三步 安装fastdfs 
   1、 上传fastdfs的安装包和libfastcommon的安装包
   2、 解压libfastcommon 安装libfastcommon
   3、 解压fastdfs 安装fastdfs
   4、 拷贝fastdfs目录中的http.conf和mime.types到/etc/fdfs 目录中
注:6台机器全部执行这些操作


第四步:部署两个tracker server服务器,需要做的工作:

    修改两个tracker服务器的配置文件:
    tracker.conf: 修改一个地方:
    base_path=/opt/fastdfs/tracker   #设置tracker的数据文件和日志目录(需预先创建)
    启动tracker服务器 fdfs_trackerd /etc/fdfs/tracker.conf


第五步 修改两个组中的4台storage中storage.conf文件
    第一组group1的第一个storage server(修改storage.conf配置文件):
    group_name=group1   #组名,根据实际情况修改,值为 group1 或 group2
    base_path=/opt/fastdfs/storage   #设置storage的日志目录(需预先创建)
    store_path0=/opt/fastdfs/storage/files    #存储路径
    tracker_server=192.168.171.135:22122  #tracker服务器的IP地址以及端口号
    tracker_server=192.168.171.136:22122

    第二组group2的第一个storage server(修改storage.conf配置文件):
    group_name=group2   #组名,根据实际情况修改,值为 group1 或 group2
    base_path=/opt/fastdfs/storage   #设置storage的日志目录(需预先创建)
    store_path0=/opt/fastdfs/storage/files    #存储路径
    tracker_server=192.168.171.135:22122  #tracker服务器的IP地址以及端口号
    tracker_server=192.168.171.136:22122
   
    启动storage服务器
    使用之前的Java代码测试FastDFS的6台机器是否可以上传文件
注意:FastDFS默认是带有负载均衡策略的可以在tracker的2台机器中修改tracker.conf文件
    store_lookup=1

    0 随机存放策略
    1 指定组
    2 选择磁盘空间的优先存放 默认值

    修改后重启服务
    fdfs_trackerd /etc/fdfs/tracker.conf restart


======================使用Nginx进行负载均衡==============================


第六步 安装 nginx ,使用nginx 对fastdfs 进行负载均衡 
    上传 nginx-1.12.2.tar.gz以及 nginx的fastdfs扩展模块安装包fastdfs-nginx-module-master.zip
    添加nginx的安装依赖
       yum install gcc openssl openssl-devel pcre pcre-devel zlib zlib-devel -y
    解压nginx
       tar -zxvf  nginx-1.12.2.tar.gz
    解压fastdfs扩展模块
       unzip fastdfs-nginx-module-master.zip
    配置nginx的安装信息
       2台tracker服务器的配置信息(不需要fastdfs模块)
         ./configure --prefix=/usr/local/nginx_fdfs
       4台storage服务器其的配置信息(需要使用fastdfs模块)
         ./configure --prefix=/usr/local/nginx_fdfs --add-module=/root/fastdfs-nginx-module-master/src
    编译并安装nginx
       ./make
       ./make install

    4台storage的服务器需要拷贝mod_fastdfs文件
    将/root/fastdfs-nginx-module-master/src目录下的mod_fastdfs.conf文件拷贝到 /etc/fdfs/目录下,这样才能正常启动Nginx;



第七步 配置tracker 的两台机器的nginx
    进入安装目录
    cd /usr/local/nginx_fdfs

    添加一个location 对请求进行拦截,配置一个正则规则 拦截fastdfs的文件路径, 并将请求转发到其余的4台storage服务器(修改 conf目录下nginx.conf 文件)
    #nginx拦截请求路径:
    location ~ /group[1-9]/M0[0-9] {   
        proxy_pass http://fastdfs_group_server; 
    }



    添加一个upstream 执行服务的IP为 另外的4台stroage 的地址
    #部署配置nginx负载均衡:
    upstream fastdfs_group_server {  
        server 192.168.171.137:80;  
        server 192.168.171.138:80;
        server 192.168.171.139:80;  
        server 192.168.171.140:80;  
    }


第八步 配置另外4台storage的nginx添加http访问的请求路径拦截
    进入安装目录
    cd /usr/local/nginx_fdfs
    添加一个location 对请求进行拦截,配置一个正则规则 拦截fastdfs的文件路径,使用fastdfs的nginx模块转发请求(修改 conf目录下nginx.conf 文件)
    #nginx拦截请求路径:
    location ~ /group[1-9]/M0[0-9] {   
        ngx_fastdfs_module;
    }


第九步 分别修改4台storage服务器的mod_fasfdfs.conf文件(/etc/fdfs/mod_fastdfs.conf)
    #修改基本路径,并在指定路径创建对应文件夹
    base_path=/opt/fastdfs/nginx_mod #保存日志目录
    #指定两台tracker服务器的ip和端口
    tracker_server=192.168.171.135:22122  #tracker服务器的IP地址以及端口号
    tracker_server=192.168.171.136:22122
    #指定storage服务器的端口号
    storage_server_port=23000 #通常情况不需要修改
    #指定当前的storage服务器所属的组名 (当前案例03和04为group1 05和06为group2)
    group_name=group1  #当前服务器的group名
    #指定url路径中是否包含组名 (当前案例url包含组名)
    url_have_group_name=true     #文件url中是否有group名
    store_path_count=1           #存储路径个数,需要和store_path个数匹配(一般不用改)
    store_path0=/opt/fastdfs/storage/files    #存储路径
    #指定组个数,根据实际配置决定,(当前案例拥有2个组group1和group2)
    group_count = 2                   #设置组的个数



    在末尾增加2个组的具体信息:
    [group1]
    group_name=group1
    storage_server_port=23000
    store_path_count=1
    store_path0=/opt/fastdfs/storage/files

    [group2]
    group_name=group2
    storage_server_port=23000
    store_path_count=1
    store_path0=/opt/fastdfs/storage/files

    第一个组的第二个storage按照相同的步骤操作;

    另外一个组的两个storage也按照相同的步骤操作;

    #测试nginx的配置文件是否正确(测试全部6台服务器)
       /usr/local/nginx_fdfs/sbin/nginx -c /usr/local/nginx_fdfs/conf/nginx.conf -t
    #启动nginx服务器(全部6台服务器)
      /usr/local/nginx_fdfs/sbin/nginx -c /usr/local/nginx_fdfs/conf/nginx.conf



测试:使用浏览器分别访问 6台 服务器中的fastdfs文件


第十步:部署前端用户访问入口服务器,即访问192.168.230.128上的Nginx,该Nginx负载均衡到后端2个tracker server;
    配置nginx.conf文件
    location ~ /group[1-9]/M0[0-9] {   
        proxy_pass http://fastdfs_group_server; 
    }


    添加一个upstream 执行服务的IP为 2台tracker 的地址
    #部署配置nginx负载均衡:
    upstream fastdfs_group_server {  
        server 192.168.171.135:80;  
        server 192.168.171.136:80; 
    }

测试:使用浏览器访问128(唯一入口的nginx服务器)服务器中的fastdfs文件
注意:由于之前128的nginx中可能拥有静态资源拦截会导致访问不到文件,这时可以注释或删除这些静态资源拦截


==============================补充资料============================================
最后,为了让服务能正常连接tracker,请关闭所有机器的防火墙:
systemctl status firewalld   查看防火墙状态
systemctl disable firewalld  禁用开机启动防火墙
systemctl stop firewalld     停止防火墙
systemctl  restart  network  重启网络
systemctl  start network     启动网络
systemctl  stop  network     停止网络

可能安装的linux(无图形的)没有开启网卡服务,可以修改/etc/sysconfig/network-scripts 下的网卡配置文件设置 ONBOOT=yse 
表示开机启动网卡,然后启动网络服务即可

Keepalived当主nginx出现故障后会自动切换到备用nginx服务器的一款软件 通常由运维人员进行使用

来源:https://www.cnblogs.com/luisblog/p/16849607.html
本站部分图文来源于网络,如有侵权请联系删除。

未经允许不得转载:百木园 » FastDFS

相关推荐

  • 暂无文章