高可用高性能分布式文件系统FastDFS实践Java程序

高可用高性能分布式文件系统FastDFS实践Java程序

大家好,又见面了,我是全栈君。

  在前篇 高可用高性能分布式文件系统FastDFS进阶keepalived+nginx对多tracker进行高可用热备 中已介绍搭建高可用的分布式文件系统架构。

  那怎么在程序中调用,其实网上有很多栗子,这里在他们的基础上作个简单的介绍。

下载源码并加入本地仓库

官网Java客户端源代码:https://github.com/happyfish100/fastdfs-client-java  

打开源码后 执行maven install 将代码打成jar到本地maven仓库(这步可自行 google)

示例源码

然后创建一个Demo工程,这里采用spring mvc模式创建。

在pom中加入引用 maven中依赖jar包

<!– fastdfs上传下载图片 路径和上面的pom中对应 –>

<dependency>
    <groupId>org.csource</groupId>
    <artifactId>fastdfs-client-java</artifactId>
    <version>1.27-SNAPSHOT</version>
</dependency>

fastdfs-client.properties

在resources的properties文件夹中创建配置文件fastdfs-client.properties

fastdfs.connect_timeout_in_seconds = 5
fastdfs.network_timeout_in_seconds = 30
fastdfs.charset = UTF-8
fastdfs.http_anti_steal_token = false
fastdfs.http_secret_key = FastDFS1234567890
fastdfs.http_tracker_http_port = 80

fastdfs.tracker_servers = 10.0.11.201:22122,10.0.11.202:22122,10.0.11.203:22122

 

 创建FastDFSClient工具类

package com.james.utils;

import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.URLDecoder;

/**
 * Created by James on 2015/11/14.
 * FastDFS文件上传
 */
public class FastDFSClientUtils {
    private TrackerClient trackerClient = null;
    private TrackerServer trackerServer = null;
    private StorageServer storageServer = null;
    private StorageClient1 storageClient = null;

    public FastDFSClientUtils(String conf) throws Exception {
        if (conf.contains("classpath:")) {
            String path = this.getClass().getResource("/").getPath();
            conf = conf.replace("classpath:", URLDecoder.decode(path, "UTF-8"));
        }
        ClientGlobal.init(conf);
        trackerClient = new TrackerClient();
        trackerServer = trackerClient.getConnection();
        storageServer = null;
        storageClient = new StorageClient1(trackerServer, storageServer);
    }

    /**
     * 上传文件方法
     * <p>Title: uploadFile</p>
     * <p>Description: </p>
     *
     * @param fileName 文件全路径
     * @param extName  文件扩展名,不包含(.)
     * @param metas    文件扩展信息
     * @return
     * @throws Exception
     */
    public String uploadFile(String fileName, String extName, NameValuePair[] metas) {
        String result = null;
        try {
            result = storageClient.upload_file1(fileName, extName, metas);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (MyException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 上传文件,传fileName
     *
     * @param fileName 文件的磁盘路径名称 如:D:/image/aaa.jpg
     * @return null为失败
     */
    public String uploadFile(String fileName) {
        return uploadFile(fileName, null, null);
    }

    /**
     * @param fileName 文件的磁盘路径名称 如:D:/image/aaa.jpg
     * @param extName  文件的扩展名 如 txt jpg等
     * @return null为失败
     */
    public String uploadFile(String fileName, String extName) {
        return uploadFile(fileName, extName, null);
    }

    /**
     * 上传文件方法
     * <p>Title: uploadFile</p>
     * <p>Description: </p>
     *
     * @param fileContent 文件的内容,字节数组
     * @param extName     文件扩展名
     * @param metas       文件扩展信息
     * @return
     * @throws Exception
     */
    public String uploadFile(byte[] fileContent, String extName, NameValuePair[] metas) {
        String result = null;
        try {
            result = storageClient.upload_file1(fileContent, extName, metas);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (MyException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 上传文件
     *
     * @param fileContent 文件的字节数组
     * @return null为失败
     * @throws Exception
     */
    public String uploadFile(byte[] fileContent) throws Exception {
        return uploadFile(fileContent, null, null);
    }

    /**
     * 上传文件
     *
     * @param fileContent 文件的字节数组
     * @param extName     文件的扩展名 如 txt  jpg png 等
     * @return null为失败
     */
    public String uploadFile(byte[] fileContent, String extName) {
        return uploadFile(fileContent, extName, null);
    }

    /**
     * 文件下载到磁盘
     *
     * @param path   图片路径
     * @param output 输出流 中包含要输出到磁盘的路径
     * @return -1失败,0成功
     */
    public int download_file(String path, BufferedOutputStream output) {
        //byte[] b = storageClient.download_file(group, path);
        int result = -1;
        try {
            byte[] b = storageClient.download_file1(path);
            try {
                if (b != null) {
                    output.write(b);
                    result = 0;
                }
            } catch (Exception e) {
            } //用户可能取消了下载
            finally {
                if (output != null)
                    try {
                        output.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 获取文件数组
     *
     * @param path 文件的路径 如group1/M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg
     * @return
     */
    public byte[] download_bytes(String path) {
        byte[] b = null;
        try {
            b = storageClient.download_file1(path);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (MyException e) {
            e.printStackTrace();
        }
        return b;
    }

    /**
     * 删除文件
     *
     * @param group       组名 如:group1
     * @param storagePath 不带组名的路径名称 如:M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg
     * @return -1失败,0成功
     */
    public Integer delete_file(String group, String storagePath) {
        int result = -1;
        try {
            result = storageClient.delete_file(group, storagePath);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (MyException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * @param storagePath 文件的全部路径 如:group1/M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg
     * @return -1失败,0成功
     * @throws IOException
     * @throws Exception
     */
    public Integer delete_file(String storagePath) {
        int result = -1;
        try {
            result = storageClient.delete_file1(storagePath);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (MyException e) {
            e.printStackTrace();
        }
        return result;
    }
}

Java客户端文件上传、下载、删除和元数据获取测试:

package com.james.fdfs;
import org.junit.Test;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import  com.james.utils.FastDFSClientUtils;

public class FastDFSClientUtilsTest {

    /**
     * 文件上传测试
     */
    @Test
    public void testUpload() {
        File file = new File("C:\\Users\\James\\Pic\\share.jpg");
        Map<String,String> metaList = new HashMap<String, String>();
        metaList.put("width","1024");
        metaList.put("height","768");
        String fid = FastDFSClientUtils.uploadFile(file,file.getName(),metaList);
        System.out.println("upload local file " + file.getPath() + " ok, fileid=" + fid);
        //上传成功返回的文件ID: group1/M00/00/00/wKgAyVgFk9aAB8hwAA-8Q6_7tHw351.jpg
    }

    /**
     * 文件下载测试
     */
    @Test
    public void testDownload() {
        int r = FastDFSClientUtils.download_file("group1/M00/00/00/wKgAyVgFk9aAB8hwAA-8Q6_7tHw351.jpg", new File("DownloadFile_fid.jpg"));
        System.out.println(r == 0 ? "下载成功" : "下载失败");
    }


    /**
     * 文件删除测试
     */
    @Test
    public void testDelete() {
        int r = FastDFSClientUtils.delete_file("group1/M00/00/00/wKgAyVgFk9aAB8hwAA-8Q6_7tHw351.jpg");
        System.out.println(r == 0 ? "删除成功" : "删除失败");
    }
}

 

如果没有什么问题将会看到打印的日志。

Net版本

net版本可参考另外一位网友代码:

https://github.com/huanzui/fastdfs.client.net

问题

现在分布式文件平台已经完成了搭建和代码测试,但实践过程中还是有几个问题:

1、上传到平台的文件名都是无规律的64base编码过的字符串,因此如果只作为如图片等文件存储是没有问题的,因为我们不关心其文件名,但如果作为要下载的内容,如附件,或安装包,下载时如果还是编码那无法直观的知道此文件是做什么的,是要转换为正确的文件名。

解决:关于这个问题,网上有方法是通过nginx,利用域名和FID拼出url,然后在url后面增加一个参数,指定原始文件名。

例如:http://121.14.161.48:9030/group2/M00/00/89/eQ6h3FKJf_PRl8p4AUz4wO8tqaA688.apk?attname=filename.apk

在Nginx上进行如下配置,这样Nginx就会截获url中的参数attname,在Http响应头里面加上字段 Content-Disposition “attachment;filename=$arg_attname”。

这里只提供了一个方案,具体内容其实还需要一个篇幅来介绍,有时间再写吧。

2、实际用的时候我们其实是想按业务还将不同文件放在不同的文件夹中的,比如聊天文件,文档文件,还有临时文件 有时需要定时清理的,但分布式文件平台是没法指定文件夹的。

解决:最常用的做法是自己实现一个文件对应库,将上传的文件名,时间,对应的业务等信息与最终的文件路径对应起来,这样就可以作任何逻辑了,但缺点是非常麻烦。

FastDFS没有看到保存到指定的2级目录的API,但可以保存到指定的group,可以指定某个group为哪个业务用。但这样会破坏整个FastDFS的分布式结构,造成某个group非常巨大,而且不容易扩容。实际使用时还会有其它业务的内容进入到此group。

3、大文件如何断点续传?

如果文件大于100M,则需要断点续传的功能了,FastDFS对于大文件来说是有点吃力的,但还是可以实现,根据网友提供的方案来看就是需要客户进行切片上传,并且切片字节大小小于等于storage配置的buff_size,默认是256k,这一块需要自己实现。

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/107741.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • python数字推盘_从零开始学编程做游戏:一个文科生策划的14周

    python数字推盘_从零开始学编程做游戏:一个文科生策划的14周点击”humansflee”按钮则人类移动一回合,点击”zombiesstalk”按钮则僵尸移动一回合。它们采取的寻路策略都是广度优先搜索。游戏不会结束,你可以在这个沙盒中给自己安排胜利条件。布置各种各样的场面看着它们行动,也还能支撑个半小时的乐趣,是到目前为止制作的可玩性最强的游戏……同样的,这个游戏也是一个具有充分扩展性的游戏。感染者会不会转化成僵尸?人类能不能拿到武器反击僵尸?僵…

    2025年6月22日
    4
  • 阿里的BUG智能定位神器!直接定位线上BUG,超给力!必须收藏!

    阿里的BUG智能定位神器!直接定位线上BUG,超给力!必须收藏!点击上方“阿拉奇学Java”,选择“置顶或者星标”每天早晨00点00分,与你相约!往日回顾:Java14令人期待的5大新特性,打包工具终于要来了!定位过程分析代码…

    2022年5月7日
    54
  • Option 82在校园网的应用与实现(转)[通俗易懂]

    Option 82在校园网的应用与实现(转)[通俗易懂]作者:南京政治学院上海分院教育技术中心陈晓晖经过近几年的建设,各高校大体完成了校园网基础环境建设,将建设重点逐步转移到信息资源与应用服务上,校园网的安全也越来越受重视,迫使网络管理员比以往更加需要在加强网络安全和便于使用之间找到一个平衡点。笔者认为在具备802.1x认证的硬件条件下,引入Option82构建一个权限分配清晰的校园网是一项可行的措施。 DHCPOption82说明和关键点

    2022年9月25日
    3
  • Layui的TreeTable使用

    Layui的TreeTable使用Layui官方本身是没有TreeTable的,不过有个大佬自己写了一个,这是码云地址:https://gitee.com/whvse/treetable-lay/tree/master/接下来我来说一下具体使用这个东西首先下载这个文件夹中的东西在你的web项目下将这个文件夹弄到里面去,在页面上导入这些文件&lt;linkrel="stylesheet"href="as…

    2022年4月30日
    54
  • 记使用 git clean -d -fx ” ” 命令悲催的一天(强列建议慎用)

    记使用 git clean -d -fx ” ” 命令悲催的一天(强列建议慎用)

    2022年2月10日
    93
  • java.lang.noclassdeffounderro_cannot resolve reference to bean

    java.lang.noclassdeffounderro_cannot resolve reference to bean1.首先创建项目选择版本号跟要导入的包创建项目之后,导包的时候出现了问题找了半天问题我发现是spring-cloud-starter-netflix-eureka-server默认的版本号太高,然后去Maven仓库换了个版本低的,就是下面这个<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netfl

    2022年8月20日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号