FastDFS(分布式文件管理系统)

一、简介

 

解决了大容量的文件存储和高并发访问的问题,文件存取时实现了负载均衡。

FastDFS服务端只有两个角色,tracker serverstorage server。

所有同角色服务器集群节点都是平等的,不存在主从关系(Master-Slave)。

存储服务器采用分组方式,同组内存储服务器上的文件完全相同(备份);不同分组的存储服务器管理不同的文件(扩容 RAID)。

不同组的storage server之间不会相互通信。

由storage server主动向tracker server报告状态信息,tracker server之间不会相互通信。

二、搭建环境

(1)拉取镜像

docker pull delron/fastdfs

(2)创建tracker service宿主机目录

mkdir -p /opt/fdfs/tracker

(3)创建tracker service容器,默认端口号22122

docker run -d --network=host --name tracker -v /opt/fdfs/tracker:/var/fdfs delron/fastdfs tracker

(4)创建storage service宿主机目录

mkdir -p /opt/fdfs/storage

(5)创建storage service并启动容器,默认端口号23000

docker run -d --network=host --name storage -e TRACKER_SERVER=192.168.146.130:22122 -v /opt/fdfs/storage:/var/fdfs -e GROUP_NAME=group1 delron/fastdfs storage

(6)重启问题解决

rm -rf /opt/fdfs/tracker/data/*.pid

rm -rf /opt/fdfs/storage/data/*.pid

三、文件上传

(1)导入依赖

        <dependency>
            <groupId>cn.bestwu</groupId>
            <artifactId>fastdfs-client-java</artifactId>
            <version>1.27</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>

(2)在src/main/resources目录中创建properties类型配置文件,命名不限。如:fdfs.properties

fastdfs.connect_timeout_in_seconds=10
fastdfs.network_timeout_in_seconds=30
fastdfs.charset=UTF-8
# tracker服务器地址,多个服务器,逗号分隔
fastdfs.tracker_servers=192.168.130.146:22122
# tracker服务器中配置文件tracker.conf中的http端口配置。必须相同。
fastdfs.http_tracker_http_port=8080

(3)创建工具类com.bjsxt.utils.FastDFSUtils

/**
 * FastDFS Java客户端工具
 */
public final class FastDFSUtils {
    /**
     * 定义静态属性,Properties和StorageClient
     */
    private final static Properties PROPERTIES;
    private final static StorageClient STORAGE_CLIENT;

    /**
     * 静态初始化代码块,初始化静态属性
     * 静态初始化代码块有异常如何处理?
     * 处理的时候,try。。catch。。 抛出一个Error,终止虚拟机。
     */
    static{
        try {
            PROPERTIES = new Properties();
            // 读取配置文件
            PROPERTIES.load(
                    FastDFSUtils.class
                            .getClassLoader()
                            .getResourceAsStream("fdfs.properties")
            );
            // 使用ClientGlobal初始化FastDFS客户端配置
            ClientGlobal.initByProperties(PROPERTIES);
            // 创建Tracker客户端对象
            TrackerClient trackerClient = new TrackerClient();
            // 基于Tracker客户端对象,获取Tracker服务器对象
            TrackerServer trackerServer = trackerClient.getConnection();
            // 基于Tracker服务器和客户端对象,获取Storage服务器对象
            StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
            // 创建Storage客户端对象
            STORAGE_CLIENT = new StorageClient(trackerServer, storageServer);
        }catch (Exception e){
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
     * 删除文件
     * int delete_file(String 卷名, String 路径及文件名);
     * 返回值: 0代表成功,其他数字代表错误编码
     */
    public static int remote(String group, String remote){
        try {
            return STORAGE_CLIENT.delete_file(group, remote);
        }catch (Exception e){
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * 查询某文件的元数据
     * @param group 卷名
     * @param remote 路径及文件名
     * @return 返回文件的元数据数组。发生错误返回null
     */
    public static NameValuePair[] getMetaData(String group, String remote){
        try{
            return STORAGE_CLIENT.get_metadata(group, remote);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 下载文件工具方法
     *  下载方法
     *  byte[] download_file(String 卷名, String 路径及文件名)
     *  返回要下载的文件内容
     * @param group 卷名
     * @param remote 路径及文件名
     * @return 返回下载的文件内容,发生错误返回null
     */
    public static byte[] download(String group, String remote){
        try {
            return STORAGE_CLIENT.download_file(group, remote);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 上传文件的工具方法
     * 一定保存文件到FastDFS,一定保存至少一个元数据(文件原始名称)
     * @param inputStream 要上传的文件的输入流
     * @param fileName 上传文件的原始名称
     * @param metaProperties 上传文件的元数据,成对提供,如: 名,值,名,值
     * @return
     */
    public static String[] uploadFile(InputStream inputStream, String fileName, String... metaProperties){
        try {
            int length = inputStream.available();
            byte[] datas = new byte[length];
            inputStream.read(datas, 0, length);
            // 处理元数据
            NameValuePair[] nameValuePairs = null;
            if (metaProperties.length % 2 == 0) {
                // 参数数量满足要求,开始处理
                nameValuePairs = new NameValuePair[metaProperties.length / 2 + 1];
                for (int i = 0; i < nameValuePairs.length; i = i + 2) {
                    nameValuePairs[i / 2] = new NameValuePair(metaProperties[i], metaProperties[i + 1]);
                }
            } else {
                nameValuePairs = new NameValuePair[1];
            }
            nameValuePairs[nameValuePairs.length - 1] = new NameValuePair("fileName", fileName);
            // 获取文件后缀
            String extName = getExtName(fileName);
            // 上传文件到FastDFS
            String[] result = STORAGE_CLIENT.upload_file(datas, extName, nameValuePairs);
            return result;
        }catch (Exception e){
            // 发生任何异常,上传文件失败。返回null
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 截取文件后缀
     * @param fileName
     * @return
     */
    private static String getExtName(String fileName){
        if(fileName.lastIndexOf(".") > -1){
            // 文件名称中包含字符 .
            return fileName.substring(fileName.lastIndexOf(".") + 1);
        }else{
            // 文件名称中不包含字符 .
            return "";
        }
    }

    /**
     * 提供获取Storage客户端对象的工具方法
     */
    public static StorageClient getStorageClient(){
        return STORAGE_CLIENT;
    }

    private FastDFSUtils(){}
}

(4)service层

 @Override
    public boolean upload(MultipartFile file) {
        try {
            //将文件存入到Storage service中,返回值为长度为2的数组,一个为存储的仓库,一个为存储的文件路径
            String[] strings = FastDFSUtils.uploadFile(file.getInputStream(), file.getOriginalFilename(), "owner", "admin");

            if(strings==null){
                return false;
            }
            //将文件信息存储到数据库中
            MyFile myFile = new MyFile();
            myFile.setFileName(file.getOriginalFilename());
            myFile.setGroupName(strings[0]);
            myFile.setRemoteName(strings[1]);
            myFile.setLength(file.getSize());
            myFile.setCreateTime(new Date());
            int insert = uploadMapper.insert(myFile);
            if(insert != 1){
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }

四、文件下载

(1)service层

 /**
     * 文件上传service
     * @param groupName 存储的主机
     * @param remoteName 存储的文件路径及文件名
     * @return
     */
    @Override
    public Map<String, Object> download(String groupName, String remoteName) {
        HashMap<String, Object> map = new HashMap<>();
        //获取文件二进制数组
        byte[] download = FastDFSUtils.download(groupName, remoteName);
        //获取文件元数据
        NameValuePair[] metaData = FastDFSUtils.getMetaData(groupName, remoteName);
        if(download != null && metaData != null){
            map.put("data",download);
            for (NameValuePair metaDatum : metaData) {
                if(metaDatum.getName().equals("fileName")){
                    map.put("fileName", metaDatum.getValue());
                    break;
                }
            }
        }
        return map;
    }

(2)controller

    @RequestMapping("/download")
    public void download(String groupName, String remoteName, HttpServletResponse response) throws IOException {
        Map<String, Object> download = uploadService.download(groupName, remoteName);
        // 设置响应头
        response.setContentType("application/octet-stream");
        // 设置下载文件的附件名称
        response.setHeader("content-disposition", "attachment;filename="+download.get("fileName").toString());
        // 输出要下载的文件内容到客户端
        byte[] datas = (byte[]) download.get("data");
        response.getOutputStream().write(datas, 0, datas.length);
    }

五、文件删除

 @Override
    public boolean delete(MyFile file) {
        //删除数据库文件
        int delete = uploadMapper.delete(file);
        //删除文件
        int remote = FastDFSUtils.remote(file.getGroupName(), file.getRemoteName());
        return delete>0&&remote==0;
    }

 六、文件预览

(1)Nginx简介

Nginx (engine x) 是一个高性能的HTTP和反向代理服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。

Nginx 是一个很强大的高性能Web和反向代理服务,它具有很多非常优越的特性:在连接高并发的情况下,Nginx是Apache服务不错的替代品:Nginx在美国是做虚拟主机生意的老板们经常选择的软件平台之一。

(2)代理方式

正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。

反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

两者间的区别

位置不同 正向代理,架设在客户机和目标主机之间; 反向代理,架设在服务器端;

代理对象不同 正向代理,代理客户端,服务端不知道实际发起请求的客户端; 反向代理,代理服务端,客户端不知道实际提供服务的服务端;

(3)Nginx常用场景

Http协议代理

只要支持HTTP协议访问的内容,都可以由Nginx进行代理。Nginx只支持HTTP协议的代理,其他协议不支持。

搭建虚拟主机

Nginx可以监听所安装的主机的某个端口,对外支持这个端口的HTTP访问。当接收到外部HTTP请求后把本机中资源返回给客户端。今天的课程内容就是使用Nginx的搭建虚拟主机功能,外部请求图片时,把图片信息响应给请求发。

负载均衡

Nginx可以代理多个主机,内置负载均衡策略。

(4)使用

使用http请求,ip+存储主机+存储路径及文件名

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值