(高可用)分布式文件系统FastDFS安装部署(附Java示例代码可直接应用)

部署环境准备

环境说明

操作系统 CentOS7.6
fastdfs 版本:6.01
nginx 版本:1.16.1
keepalived 版本:2.0.19

系统依赖

gcc gcc-c++ perl pcre pcre-devel zlib zlib-devel openssl openssl-devel libnl libnl-devel
yum install gcc gcc-c++
yum -y install pcre pcre-devel    
yum -y install zlib zlib-devel
yum -y install openssl openssl-devel
yum -y install libnl libnl-devel

软件环境

libevent 下载地址:http://libevent.org/
nginx 下载地址:http://nginx.org/en/download.html
keepalived 下载地址:https://www.keepalived.org/software/
fastdfs 下载地址:https://github.com/happyfish100/fastdfs/releases
libfasttcommon 下载地址:https://github.com/happyfish100/libfastcommon/releases
fastdfs-nginx-module 下载地址:https://github.com/happyfish100/fastdfs-nginx-module/releases

机器及网络环境规划

准备2台机器

Fdfs Server VIP:192.168.2.222
Tracker Server1:192.168.2.132
Tracker Server2:192.168.2.133
Storage Group1 Node1:192.168.2.132
Storage Group1 Node2:192.168.2.133

防火墙设置

关闭系统防火墙:

sudo systemctl stop firewalld && systemctl disable firewalld

Keepalived 服务安装配置

下载 Keepalived 源码包

官网地址:https://www.keepalived.org/
下载地址:https://www.keepalived.org/software/keepalived-2.0.19.tar.gz

#上传并解压 Keepalived 源码包
tar -zxvf keepalived-2.0.19.tar.gz
#编译 Keepalived 准备
进入解压目录:cd keepalived-2.0.19
#执行编译准备:
./configure -prefix=/work/keepalived

注意:一定要有 gcc 和 openssl 编译相关的依赖

编译安装 Keepalived

make && make install

安装配置 Keepalived

keepalived 启动时会从 /etc/keepalived/ 中相关的目录下查找 keepalived.conf 配置文件,因此将 keepalived 安装目录 /usr/local/keepalived/etc/keepalived.conf 拷贝到 /etc/keepalived/ 中。

mkdir /etc/keepalived/
cp /work/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf
cp /work/keepalived/etc/sysconfig/keepalived /etc/sysconfig/keepalived

设置 Keepalived 开机启动项

systemctl enable keepalived

然后就能使用 systemctl start/stop/status keepalived 管理 keepalived 了

配置 Keepalived 服务

配置文件参数自行百度,自行修改添加
192.168.2.132 机器上配置:

vi /etc/keepalived/keepalived.conf
vrrp_script check_nginx {
       interval 3
       script "/work/script/check_nginx.sh"
}

vrrp_instance fdfs_server {
       state MASTER
       interface enp0s3
       virtual_router_id 110
       priority 100
       advert_int 3
       authentication {
            auth_type PASS
            auth_pass password
       }
       virtual_ipaddress {
            192.168.100.110
       }
       track_script {
            check_nginx
       }
}

192.168.2.133 机器上配置:

vi /etc/keepalived/keepalived.conf
vrrp_script check_nginx {
       interval 3
       script "/work/script/check_nginx.sh"
}

vrrp_instance fdfs_server {
       state BACKUP
       interface enp0s3
       virtual_router_id 110
       priority 90
       advert_int 3
       authentication {
            auth_type PASS
            auth_pass password
       }
       virtual_ipaddress {
            192.168.100.110
       }
       track_script {
            check_nginx
       }
}

编写 nginx 服务检测脚本

vi /work/script/check_nginx.sh
#!/bin/bash
active_status=`netstat -lntp|grep nginx|wc -l`
if [ $active_status -gt 0 ]; then
    exit 0
else
    exit 1
fi
#然后给脚本赋予执行权限:
chmod +x /work/script/check_nginx.sh
#修改内核参数
vi /etc/sysctl.conf
#增加如下内容:
net.ipv4.ip_nonlocal_bind = 1  #允许忽视VIP的存在
net.ipv4.ip_forward = 1  #允许转发
#使配置生效
sysctl –p 

安装 FastDFS 依赖库

安装 libevent 依赖

#解压 libevent 源码包:
tar -zxvf libevent-2.1.11-stable.tar.gz
#进入源码目录:
cd libevent-2.1.11-stable
#编译安装前配置:
./configure
#编译安装:
make && make install

默认安装位置:
/usr/local/lib

安装 libfasttcommon 依赖

#解压 libfasttcommon 源码包:
tar -zxvf libfastcommon-1.0.41.tar.gz
#进入源码目录:
cd libfastcommon-1.0.41
#编译安装:
./make.sh && ./make.sh install

默认安装位置:/usr/lib64

安装部署 Tracker 服务和 Storage 服务

安装 fastdfs 服务
#解压 fastdfs 源码包:
tar -zxvf fastdfs-6.01.tar.gz
#进入 fastdfs 源码包:
cd fastdfs-6.01
#编译安装:
./make.sh && ./make.sh install

fastdfs 服务目录信息

安装完成后服务及脚本拷贝到 /usr/bin 目录,配置文件拷贝到 /etc/fdfs 目录,启动脚本拷贝到 /etc/init.d/ 目录

#注册开机启动
chkconfig --add fdfs_trackerd
chkconfig fdfs_trackerd on
chkconfig --add fdfs_storaged
chkconfig fdfs_storaged on

数据目录规划

#创建 fdfs 数据主目录:
mkdir /work/fdfs
#创建 tracker 数据目录:
mkdir /work/fdfs/tracker
#创建 storage 数据目录:
mkdir /work/fdfs/storage

配置 tracker 服务

#将 /etc/fdfs 目录下的 tracker.conf.sample 改为 tracker.conf:
mv tracker.conf.sample tracker.conf 
#修改内容如下:
将 
base_path=/home/yuqing/fastdfs 
改为:
/work/fdfs/tracker (该目录为上面定义创建)
#启动 Tracker 服务:
systemctl start fdfs_trackerd

配置 storage 服务

#将 /etc/fdfs 目录下的 storage.conf.sample 改为 storage.conf:
mv storage.conf.sample 
#storage.conf 修改内容如下:
base_path=/home/yuqing/fastdfs 改为:base_path=/work/fdfs/storage (该目录为上面定义创建)
store_path0=/home/yuqing/fastdfs 改为:store_path0=/work/fdfs/storage (该目录为上面定义创建)
tracker_server=192.168.209.121:22122 改为:tracker_server=192.168.2.132:22122 和 tracker_server=192.168.2.133:22122
#将 /etc/fdfs/torage_ids.conf.sample 为 storage_ids.conf,
mv storage_ids.conf.sample storage_ids.conf 
#内容修改为当前 group 的 storage 节点信息:
100001 group1 192.168.2.132
100002 group1 192.168.2.133
#启动 Storage 服务:
systemctl start fdfs_storaged

Nginx 服务安装配置

下载 Nginx 源码包

官网地址:http://nginx.org/
下载地址:http://nginx.org/en/download.html

#上传并解压 Nginx 源码包及 fastdfs 插件包
tar -zxvf nginx-1.16.1.tar.gz
tar -zxvf fastdfs-nginx-module-1.21.tar.gz
#编译 Nginx 准备
#进入解压目录:cd nginx-1.16.1
#拷贝 fastdfs 插件包到 nginx 源码目录:
mv ../fastdfs-nginx-module-1.21 .
#执行编译准备:
./configure --prefix=/work/nginx \--with-stream \--add-module=fastdfs-nginx-module-1.21/src
#注意:一定要有 gcc 和 openssl 编译相关的依赖

#编译安装 Nginx
make && make install

#注册到系统服务
vi /usr/lib/systemd/system/nginx.service
[Unit]
Description=nginx
Documentation=http://nginx.org/en/docs/
After=network.target

[Service]
Type=forking
PIDFile=/work/nginx/logs/nginx.pid
ExecStartPre=/work/nginx/sbin/nginx -t -c /work/nginx/conf/nginx.conf
ExecStart=/work/nginx/sbin/nginx -c /work/nginx/conf/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
#设置 Nginx 开机启动项
systemctl enable nginx
#然后就能使用 systemctl start/stop/status nginx 管理 nginx 了

#修改 Nginx 配置
vi /work/nginx/conf/nginx.conf
#user  nobody;
worker_processes  1;
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}
# tracker 负载均衡配置
stream {

    upstream tracker {
         server 192.168.2.132:22122 weight=1 max_fails=2 fail_timeout=10s;
         server 192.168.2.133:22122 weight=1 max_fails=2 fail_timeout=10s;
    }

    server {
        listen       7777;
        proxy_timeout 5m;
        proxy_pass tracker;
        proxy_connect_timeout 10s;
    }

}

http {

    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    
	# storage 负载均衡配置
    upstream storage {
         server 192.168.2.132:8888 weight=1 max_fails=2 fail_timeout=10s;
         server 192.168.2.133:8888 weight=1 max_fails=2 fail_timeout=10s;
    }

    server {
        listen       80;
        server_name  localhost;
        
        location /group1 {
            proxy_pass         http://storage;
            proxy_set_header   Host             $host;
            proxy_set_header   X-Real-IP        $remote_addr;
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        }
        
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

    server {
        listen       8888;
        server_name  localhost;

        location / {
            alias /work/fdfs/storage/data/;
            ngx_fastdfs_module;
        }
    }

}

配置 nginx 的 fdfs 插件

#将fastdfs安装目录下的http.conf和mime.types拷贝到/etc/fdfs
cd ~
cd fastdfs-6.01
cd conf
cp http.conf /etc/fdfs/http.conf
cp mime.types  /etc/fdfs/mime.types

将 fastdfs-nginx-module-1.21/src 下的 mod_fastdfs.conf 拷贝到 /etc/fdfs 下

修改 mod_fastdfs.conf 如下:
连接超时时间: connect_timeout=5
Tracker 服务地址:tracker_server=192.168.2.132:22122 和 tracker_server=192.168.2.133:22122
Storage 服务端口:storage\server_port=23000
如果文件 ID 的 uri 中包含 /group**,则要设置为 true:url_have_group_name = true
Storage 配置的 store_path0 路径,必须和 storage.conf 中的一致:store_path0=/work/fdfs/storage
其他详细配置如下:

# connect timeout in seconds
# default value is 30s
connect_timeout=5

# network recv and send timeout in seconds
# default value is 30s
network_timeout=10

# the base path to store log files
base_path=/work/fdfs/storage

# if load FastDFS parameters from tracker server
# since V1.12
# default value is false
load_fdfs_parameters_from_tracker=true

# storage sync file max delay seconds
# same as tracker.conf
# valid only when load_fdfs_parameters_from_tracker is false
# since V1.12
# default value is 86400 seconds (one day)
storage_sync_file_max_delay = 86400

# if use storage ID instead of IP address
# same as tracker.conf
# valid only when load_fdfs_parameters_from_tracker is false
# default value is false
# since V1.13
use_storage_id = false

# specify storage ids filename, can use relative or absolute path
# same as tracker.conf
# valid only when load_fdfs_parameters_from_tracker is false
# since V1.13
storage_ids_filename = storage_ids.conf

# FastDFS tracker_server can ocur more than once, and tracker_server format is
#  "host:port", host can be hostname or ip address
# valid only when load_fdfs_parameters_from_tracker is true
tracker_server=192.168.2.132:22122
tracker_server=192.168.2.133:22122

# the port of the local storage server
# the default value is 23000
storage_server_port=23000

# the group name of the local storage server
group_name=group1

# if the url / uri including the group name
# set to false when uri like /M00/00/00/xxx
# set to true when uri like ${group_name}/M00/00/00/xxx, such as group1/M00/xxx
# default value is false
url_have_group_name = true

# path(disk or mount point) count, default value is 1
# must same as storage.conf
store_path_count=1

# store_path#, based 0, if store_path0 not exists, it's value is base_path
# the paths must be exist
# must same as storage.conf
store_path0=/work/fdfs/storage

# standard log level as syslog, case insensitive, value list:
### emerg for emergency
### alert
### crit for critical
### error
### warn for warning
### notice
### info
### debug
log_level=info

# set the log filename, such as /usr/local/apache2/logs/mod_fastdfs.log
# empty for output to stderr (apache and nginx error_log file)
log_filename=

# response mode when the file not exist in the local file system
## proxy: get the content from other storage server, then send to client
## redirect: redirect to the original storage server (HTTP Header is Location)
response_mode=proxy

# the NIC alias prefix, such as eth in Linux, you can see it by ifconfig -a
# multi aliases split by comma. empty value means auto set by OS type
# this paramter used to get all ip address of the local host
# default values is empty
if_alias_prefix=

# use "#include" directive to include HTTP config file
# NOTE: #include is an include directive, do NOT remove the # before include
#include http.conf

# if support flv
# default value is false
# since v1.15
flv_support = true

# flv file extension name
# default value is flv
# since v1.15
flv_extension = flv

# set the group count
# set to none zero to support multi-group on this storage server
# set to 0  for single group only
# groups settings section as [group1], [group2], ..., [groupN]
# default value is 0
# since v1.14
group_count = 1

# group settings for group #1
# since v1.14
# when support multi-group on this storage server, uncomment following section
[group1]
group_name=group1
storage_server_port=23000
store_path_count=1
store_path0=/work/fdfs/storage

# group settings for group #2
# since v1.14
# when support multi-group, uncomment following section as neccessary
#[group2]
#group_name=group2
#storage_server_port=23000
#store_path_count=1
#store_path0=/home/yuqing/fastdfs

服务启动及验证

分别启动 keepalive、nginx、tracker、storage 服务
查看服务是否正常服务:

ps -ef|grep keepalive
ps -ef|grep nginx
ps -ef|grep tracker
ps -ef|grep storage

在任意 Storage 机器上查看集群状态:

fdfs_monitor /etc/fdfs/storage.conf

Java 客户端测试

环境准备

目前有两个客户端可以选择。一个是
com.github.tobato.fastdfs-client
优点:使用简单,社区活跃。
缺点:实测bug有点多…

一个是 fastdfs作者自己搞的
下载并编译 官方 fastdfs-client-java
下载地址:https://gitee.com/fastdfs100/fastdfs-client-java

我们这里使用第二种。然后源码编译根据自己实际情况对源码进行改进优化。

#将源码clone到本地
git clone https://gitee.com/fastdfs100/fastdfs-client-java.git
#如果不需要修改源码,直接编译安装
mvn clean install
#编译完成后会生成一个jar,我们可以提交到自己的maven私服管理或者直接在程序里引入第三方jar
#fastdfs-client-java-1.29-SNAPSHOT.jar

我们直接引入jar的方式

集成

新建一个SpringBoot 工程。版本我用的是2.1.17

将fastdfs-client-java-1.29-SNAPSHOT.jar 放到resources/jar下。

在pom.xml增加依赖:

    <dependency>
        <groupId>org.csource</groupId>
        <artifactId>fastdfs-client-java</artifactId>
        <version>1.29-SNAPSHOT</version>
        <scope>system</scope>
        <systemPath>${project.basedir}/src/main/resources/jar/fastdfs-client-java-1.29-SNAPSHOT.jar</systemPath>
    </dependency>
添加fdfs_client.conf

在resources下新建fdfs_client.conf(名字可以自定义)内容如下

#fdfs_client.conf 配置
connect_timeout = 10
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 80
http.anti_steal_token = no
# 凭证,与源码中保持一致
http.secret_key = FastDFS1234567890

connection_pool.enabled = true
connection_pool.max_count_per_entry = 500
connection_pool.max_idle_time = 3600
connection_pool.max_wait_time_in_ms = 1000

# tracker 服务列表,因为我们之前用了nginx+keepalived,这里使用虚拟VIP 端口是7777
tracker_server = 192.168.2.222:7777

核心工具类FastDFSClientUtils,源码如下:

package com.sinotn.common.utils;

import com.sinotn.common.exception.BaseException;
import com.sinotn.common.utils.file.FileUploadUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * @Author: libin
 * @CreateTime: 2020-09-28 14:08
 * @Description: FastDFS分布式文件系统操作客户端
 */
@Slf4j
@Component
public class FastDFSClientUtils {

    private static TrackerClient trackerClient;

    @PostConstruct
    public void init() {
        // resources 下fdfs_client.conf 如果改名字这里也要改,否则加载默认的conf
        URL url = Thread.currentThread().getContextClassLoader().getResource("fdfs_client.conf");
        if (null == url) {
            log.error("加载FastDFS配置文件失败,请检查配置文件名是否一致");
        } else {
            String configName = url.getPath();
            try {
                ClientGlobal.init(configName);
                TrackerGroup trackerGroup = ClientGlobal.g_tracker_group;
                trackerClient = new TrackerClient(trackerGroup);
                log.info("加载FastDFS配置文件[{}]成功", configName);
            } catch (Exception e) {
                log.error("加载FastDFS配置文件[{}]失败:{}", configName, e);
            }
        }
    }

    /**
     * 上传文件
     *
     * @param file     文件
     * @param metaList 附属信息
     * @return 上传成功返回文件id,失败返回null
     */
    public String uploadFile(File file, NameValuePair[] metaList) throws Exception {
        if (!file.exists()) {
            throw new FileNotFoundException();
        }
        FileInputStream fis = null;
        try {
            String fileName = file.getName();
            String fileType = metaList[2].getValue();
            log.info("[FastDFS-upload]-start upload file ... ");
            log.info("[FastDFS-upload]-request upload file name: {}", fileName);
            log.info("[FastDFS-upload]-request upload file info: {}", Arrays.toString(metaList));
            fis = new FileInputStream(file);
            byte[] fileBuff = null;
            if (null != fis) {
                int len = fis.available();
                fileBuff = new byte[len];
                fis.read(fileBuff);
            }
            StorageClient1 storageClient1 = getStoreClient1(null);
            String fileId = storageClient1.upload_file1(fileBuff, fileType, metaList);
            log.info("[FastDFS-upload]-upload success path: {}", fileId);
            return fileId;
        } catch (Exception e) {
            log.error("[FastDFS-upload]-upload file exception info: {}", e.getMessage());
            throw new BaseException("FastDFSClientUtils uploadFile ex :{}", e.getMessage());
        } finally {
            IOUtils.closeQuietly(fis);
        }
    }

    /**
     * 下载方法
     *
     * @param fileId 文件id
     * @return 文件信息map file_data:文件byte数组 file_name:文件名
     */
    public Map<String, Object> downloadFile(String fileId) {
        HashMap<String, Object> resultMap = new HashMap<>(2);
        try {
            String groupName = getGroupName(fileId);
            log.info("[FastDFS-download]-start download file ... ");
            log.info("[FastDFS-download]-request download file group: {}", groupName);
            log.info("[FastDFS-download]-request download file path: {}", fileId);
            StorageClient1 storageClient1 = getStoreClient1(groupName);
            byte[] bytes = storageClient1.download_file1(fileId);
            if (null == bytes) {
                throw new BaseException("资源文件不存在");
            }
            // 文件byte数组
            resultMap.put("file_data", bytes);
            NameValuePair[] metaList = getFileMetaList(fileId);
            if (null == metaList) {
                throw new BaseException("获取文件元信息为空,可能是未按照规定上传文件");
            }
            // 下载文件名
            resultMap.put("file_name", metaList[2].getValue());
            log.info("[FastDFS-download]-request download file success ... ");
            return resultMap;
        } catch (Exception e) {
            log.error("[FastDFS-download]-upload file exception info: {}", e.getMessage());
            throw new BaseException("FastDFSClientUtils downloadFile ex :{}", e.getMessage());
        }
    }

    /**
     * 删除文件
     *
     * @param fileId 文件id
     * @return 删除成功返回0,非0则操作失败,返回错误代码
     */
    public int deleteFile(String fileId) {
        try {
            String groupName = getGroupName(fileId);
            log.info("[FastDFS-delete]-start delete file ... ");
            log.info("[FastDFS-delete]-request delete file group: {}", groupName);
            log.info("[FastDFS-delete]-request delete file path: {}", fileId);
            StorageClient1 storageClient1 = getStoreClient1(groupName);
            int result = storageClient1.delete_file1(fileId);
            log.info("[FastDFS-delete]-request delete file success ... {}", result);
            return result;
        } catch (Exception e) {
            log.error("[FastDFS-delete]-upload file exception info: {}", e.getMessage());
            throw new BaseException("FastDFSClientUtils deleteFile ex :{}", e.getMessage());
        }
    }

    /**
     * 查看文件元信息
     *
     * @param fileId 文件id
     * @return 文件元信息
     */
    public NameValuePair[] getFileMetaList(String fileId) {
        try {
            String groupName = getGroupName(fileId);
            log.info("[FastDFS-meta]-start meta file ... ");
            log.info("[FastDFS-meta]-request meta file group: {}", groupName);
            log.info("[FastDFS-meta]-request meta file path: {}", fileId);
            StorageClient1 storageClient1 = getStoreClient1(groupName);
            NameValuePair[] metaList = storageClient1.get_metadata1(fileId);
            log.info("[FastDFS-meta]-request delete file success ... {}", Arrays.toString(metaList));
            return metaList;
        } catch (Exception e) {
            log.error("[FastDFS-meta]-upload file exception info: {}", e.getMessage());
            throw new BaseException("FastDFSClientUtils deleteFile ex :{}", e.getMessage());
        }
    }

    /**
     * 修改文件
     *
     * @param oldFileId 旧文件id
     * @param file      新文件
     * @param metaList  新文件附录信息
     * @return 上传成功返回id,失败返回null
     */
    public String modifyFile(String oldFileId, File file, NameValuePair[] metaList) {
        String fileId = null;
        try {
            log.info("[FastDFS-modify]-start modify file ... ");
            log.info("[FastDFS-modify]-request old file : {}", oldFileId);
            // 先上传
            fileId = uploadFile(file, metaList);
            if (fileId == null) {
                return null;
            }
            // 再删除
            int delResult = deleteFile(oldFileId);
            if (delResult != 0) {
                log.error("[FastDFS-modify]-modify file failure");
                return null;
            }

        } catch (Exception e) {
            log.error("[FastDFS-modify]-modify file exception info: {}", e.getMessage());
            throw new BaseException("FastDFSClientUtils modifyFile e :{}", e.getMessage());
        }
        log.error("[FastDFS-modify]-modify file success....{}", fileId);
        return fileId;
    }


    /**
     * 组织文件自定义附录信息
     * 可以自定义添加一些辅助信息
     * @param file 文件对象
     * @return 返回组织好的附录信息
     */
    public NameValuePair[] createFileRemarks(MultipartFile file) {
        NameValuePair[] metaList = new NameValuePair[5];
        // 获取文件名:方法自行实现
        metaList[0] = new NameValuePair("file_name", FileUploadUtils.extractFilenameNotDir(file));
        metaList[1] = new NameValuePair("file_size", String.valueOf(file.getSize()));
        // 获取文件类型:方法自行实现
        metaList[2] = new NameValuePair("file_type", FileUploadUtils.getExtension(file));
        metaList[3] = new NameValuePair("create_user", "上传者");
        metaList[4] = new NameValuePair("create_time", DateUtils.parseLocalDateTimeToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, LocalDateTime.now()));
        return metaList;
    }

    /**
     * 获取存储客户端
     *
     * @param group 组名
     * @return 存储客户端对象
     */
    private static StorageClient1 getStoreClient1(String group) throws MyException, IOException {
        TrackerServer trackerServer = trackerClient.getTrackerServer();
        if (null == trackerServer) {
            throw new BaseException("FastDFSClientUtils getTrackerServer is null");
        }
        StorageServer storageServer = null;
        if (StringUtils.isEmpty(group)) {
            storageServer = trackerClient.getStoreStorage(trackerServer);
        } else {
            storageServer = trackerClient.getStoreStorage(trackerServer, group);
        }

        if (null == storageServer) {
            throw new BaseException("FastDFSClientUtils getStoreStorage is null");
        }
        return new StorageClient1(trackerServer, storageServer);
    }

    /**
     * 获取文件组名
     *
     * @param fileId 文件ID
     * @return 文件组名
     */
    private static String getGroupName(String fileId) {
        String[] paths = fileId.split("/");
        if (paths.length == 1) {
            throw new BaseException("解析文件路径错误,有效的路径样式为(group/path) 而当前解析路径为".concat(fileId));
        } else {
            String[] var2 = paths;
            int var3 = paths.length;

            for (int var4 = 0; var4 < var3; ++var4) {
                String item = var2[var4];
                if (item.indexOf("group") != -1) {
                    return item;
                }
            }
            throw new BaseException("解析文件路径错误,被解析路径url没有group,当前解析路径为".concat(fileId));
        }
    }

}

核心入口类FastDFSController,源码如下:

package com.sinotn.web.common;

import com.sinotn.common.annotation.Log;
import com.sinotn.common.core.domain.AjaxResult;
import com.sinotn.common.enums.BusinessType;
import com.sinotn.common.exception.file.FileNameLengthLimitExceededException;
import com.sinotn.common.utils.FastDFSClientUtils;
import com.sinotn.common.utils.MultipartFileToFile;
import com.sinotn.common.utils.file.FileUploadUtils;
import com.sinotn.common.utils.file.MimeTypeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.csource.common.NameValuePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.OutputStream;
import java.util.Map;

/**
 * @Author: libin
 * @CreateTime: 2020-09-28 14:54
 * @Description: FastDFS分布式文件系统入口
 */
@RestController
@Slf4j
@RequestMapping("/dfs")
public class FastDFSController {

    @Autowired
    private FastDFSClientUtils fastDFSClientUtils;

    /**
     * 分布式文件上传请求
     */
    @PostMapping("/upload")
    public AjaxResult uploadFile(MultipartFile file) {
        File tempFile = null;
        try {
            // 一系列文件校验

            // 组织文件附录信息
            NameValuePair[] metaList = fastDFSClientUtils.createFileRemarks(file);
            tempFile = MultipartFileToFile.multipartFileToFile(file);
            String fileId = fastDFSClientUtils.uploadFile(tempFile, metaList);
            return AjaxResult.success(fileId);
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.error(e.getMessage());
        } finally {
            // 删除本地临时文件
            MultipartFileToFile.deleteTempFile(tempFile);
        }
    }


    /**
     * 分布式文件下载请求
     *
     * @param fileUrl 文件地址
     * @param delete  是否删除
     */
    @GetMapping("/download")
    public void fileDownload(@RequestParam String fileUrl, @RequestParam Boolean delete, HttpServletResponse response, HttpServletRequest request) {
        Map<String, Object> resultMap = fastDFSClientUtils.downloadFile(fileUrl);
        byte[] bytes = (byte[]) resultMap.get("file_data");
        String fileName = (String) resultMap.get("file_name");
        OutputStream out = null;
        try  {
            response.setCharacterEncoding("utf-8");
            response.setContentType("multipart/form-data");
            response.setHeader("Content-Disposition",
                    "attachment;fileName=" + fileName);
            out = new BufferedOutputStream(response.getOutputStream());
            out.write(bytes);
            out.flush();
            if (delete) {
                fastDFSClientUtils.deleteFile(fileUrl);
            }
        } catch (Exception e) {
            log.error("下载文件失败", e);
        } finally {
            IOUtils.closeQuietly(out);
        }
    }
    /**
     * 查看分布式文件源信息
     *
     * @param fileUrl 文件地址
     */
    @GetMapping()
    public AjaxResult getFileFdfsInfo(@RequestParam String fileUrl) {
        try {
            NameValuePair[] getFileMetaList = fastDFSClientUtils.getFileMetaList(fileUrl);
            return AjaxResult.success(getFileMetaList);
        } catch (Exception e) {
            return AjaxResult.error(e.getMessage());
        }
    }
    /**
     * 删除文件
     *
     * @param fileUrl 文件地址
     */
    @DeleteMapping()
    public AjaxResult deleteFile(@RequestParam String fileUrl) {
        try {
            fastDFSClientUtils.deleteFile(fileUrl);
            return AjaxResult.success();
        } catch (Exception e) {
            return AjaxResult.error(e.getMessage());
        }
    }
}

自行测试吧~~~~~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值