FastDFS文件系统

FastDFS文件系统


✧ 什么是文件系统?
文件系统是操作系统用于在磁盘或分区上组织文件的方法和数据结构。磁盘空间是什么样的我们并不清楚,但文件系统可以给我们呈现一个非常清晰的表象,我们可以创建、删除、修改和复制这些文件,而实现这些功能的软件就是文件系统。操作系统中负责管理和存储文件信息的软件被称为文件管理系统,简称文件系统。



一、文件系统的发展阶段
  • 单击文件系统
  • 网路文件系统
  • 分布式文件系统

二、文件的发展史
  • 本地文件服务器
  • 独立文件服务器
  • 分布式文件服务器

三、FastDFS简介
  • 专用型分布式文件系统,fastDFS是基于C语言开发,轻量级开源的高性能分布式文件系统
  • 主要功能:文件存储,文件同步,文件访问,解决大容量的文件存储与高并发访问问题,文件存取实现了负载均衡适合中大型网站4kb-500MB之间,国产(作者余庆)

FastDFS架构和存储策略:
在这里插入图片描述
具体架构:

  • Client:客户端
  • Tracker Server:跟踪服务器
  • Storage Server:存储服务器
  • Group:组,也可称为 Volume 卷
  • Metadata:文件系统存储数据分为:数据和元数据:描述文件信息

存储策略:

​ 一个或多个组存储,每个组独立,所有组的文件容量累加就是存储系统中的文件容量,自动同步已有文件,动态添加组,2级子目录256个子目录共65536个目录


四、FastDFS的安装与使用(Linux)

环境准备我们需要模拟客户端,跟踪服务端,存储服务端,三个服务器,这里为了方便演示使用两台虚拟机进行模拟演示,其实三种服务器是一个软件,根据不同的配置文件,启动为不同的角色


1、 资源下载与依赖公共函数库的安装

① 资源下载

直接通过 Github:https://github.com/happyfish100 下载 libfastcommon , fastdfs , fastdfs-nginx-module 三个项目对应的压缩包或者使用 git 命令下载,或者通过资源地址:https://sourceforge.net/projects/fastdfs/files/ 下载。


② 安装依赖与公共函数库
将准备好的压缩包导入linux,并进行解压

//安装c++环境(因为fastdfs由C编写)
yum install -y make cmake gcc gcc-c++
# 安装 unzip 用于解压 
yum install -y unzip
# 解压 libfastcommon 
unzip libfastcommon-master.zip

在这里插入图片描述

# 进入解压后的 libfastcommon-master 目录
cd libfastcommon-master
# 编译并安装 
./make.sh && ./make.sh install

在这里插入图片描述


2、 安装 FastDFS
//解压
unzip fastdfs-master.zip
# 进入解压后的 fastdfs-master 目录 
cd fastdfs-master 
# 编译并安装、
./make.sh && ./make.sh install

在这里插入图片描述
fastdfs 默认安装在以下位置:

  • /usr/bin :可执行文件
  • /etc/fdfs :配置文件
  • /etc/init.d :主程序代码
  • /usr/include/fastdfs :插件组

以上内容两台虚拟机一致,后面将会通过不同的配置实现一个作为Tracker 跟踪服务器与Storage 存储服务器,切记两台虚拟机的防火墙关闭!(第二台虚拟机克隆后一定要修改IP)命令如下:

修改虚拟机IP
	vim /etc/sysconfig/network-scripts/ifcfg-ens33 
查看网络
  	ip addr
重启网络
    systemctl restart network
创建多级目录
    mkdir -p
//---VIM操作补充----
	set number:设置行号
    /搜索关键字:搜索内容 n切换下一个
    netstat -lnp|grep 80
    //只要ping另一台虚拟机能通,就代表防火墙关闭了

在这里插入图片描述


3、 Tracker跟踪服务器配置与启动

在这里插入图片描述

  • client.conf.sample :客户端的配置文件,测试用
  • storage.conf.sample :存储器的配置文件
  • tracker.conf.sample :跟踪器的配置文件

复制一份配置文件并修改

# 拷贝文件 tracker.conf.sample 并重命名为 tracker.conf 
cp /etc/fdfs/tracker.conf.sample /etc/fdfs/tracker.conf
# 编辑 tracker.conf 配置文件 
vim /etc/fdfs/tracker.conf

在这里插入图片描述

# 创建 tracker 服务器的运行数据和日志的存储父路径 
mkdir -p /fastdfs/tracker 
# 启动 tracker 服务 
service fdfs_trackerd start 
# 查看 tracker 服务状态 
service fdfs_trackerd status 
# 重启 tracker 服务 
service fdfs_trackerd restart 
# 停止 tracker 服务
service fdfs_trackerd stop

在这里插入图片描述

4、 Storage跟踪服务器配置与启动

复制并编辑配置文件

# 拷贝文件 storage.conf.sample 并重命名为 storage.conf 
cp /etc/fdfs/storage.conf.sample /etc/fdfs/storage.conf 
# 编辑 storage.conf 配置文件 
vim /etc/fdfs/storage.conf

修改内容:
在这里插入图片描述

//启动服务
# 创建 storage 服务器的运行数据和日志的存储父路径 
mkdir -p /fastdfs/storage/base
# 创建 storage 服务器中客户端上传的文件的存储父路径 
mkdir -p /fastdfs/storage/store 
# 启动 storage 服务 
service fdfs_storaged start 
# 查看 storage 服务状态 
service fdfs_storaged status 
# 重启 storage 服务 
service fdfs_storaged restart 
# 停止 storage 服务 
service fdfs_storaged stop

查看 /fastdfs/storage/store 目录可以看到 Storage 服务器创建了 65536 个文件夹用于存储客户端上传的文件。
在这里插入图片描述


5、配置Client✧基本使用方式

FastDFS 向使用者提供基本文件访问接口,比如 upload、download、append、delete等,以客户端库的方式提供给用户使用(增删查)


配置客户端

# 拷贝文件 client.conf.sample 并重命名为 client.conf 
cp /etc/fdfs/client.conf.sample /etc/fdfs/client.conf 
# 编辑 client.conf 配置文件 
vim /etc/fdfs/client.conf

修改内容:
在这里插入图片描述
记得 mkdir -p /fastdfs/client 创建 Client 目录


① 文件上传

在这里插入图片描述

//方式一:
[root@localhost ~]# fdfs_upload_file /etc/fdfs/client.conf ./xxx.png
//返回值:>>>
group1/M00/00/00/wKiogmGnOseACCydAAFzuvBwER8459.png

------
//方式二
[root@localhost ~]# fdfs_test /etc/fdfs/client.conf upload /usr/local/src/china.jpg


方式一
在这里插入图片描述

返回值解析

  • group1 :组名/卷名。文件上传成功以后所在的 Storage 组名称,由Storage 服务器返回。
  • M00 :虚拟磁盘路径。与 Storage 配置文件中磁盘选项 store_path* 对应。如果配置了
  • store_path0 则是 M00 ,如果配置了 store_path1 则是 M01 ,以此类推。比如:store_path0 = /fastdfs/storage/store , M00 则表示: /fastdfs/storage/store/data 。 /02/44 :数据两级目录。Storage 服务器在每个虚拟磁盘路径下创建的两级目录,用于存
    储数据文件。
  • wKgDrE34E8wAAAAAAAAGkEIYJK42378 :file_id,由 Storage Server IP、文件创建时间、文件大小、文件 crc32 和一个随机数组成,然后将这个二进制串进行 base64 编码,转换为字符串。
  • group1/M00/02/44/wKgDrE34E8wAAAAAAAAGkEIYJK42378.sh :文件名。

② 下载文件

在这里插入图片描述

//方法一:
fdfs_download_file /etc/fdfs/client.conf group1/M00/00/00/wKgKZl9smB- AVBRKAADhaCZ_RF0518.jp
//方法二
fdfs_test /etc/fdfs/client.conf download group1 M00/00/00/wKgKZl9smB- AVBRKAADhaCZ_RF0518.jpg

③ 删除存储服务器的文件

//方法一
fdfs_delete_file /etc/fdfs/client.conf group1/M00/00/00/wKgKZl9smB- AVBRKAADhaCZ_RF0518.jpg
//方法二
fdfs_test /etc/fdfs/client.conf delete group1 M00/00/00/wKgKZl9smB- AVBRKAADhaCZ_RF0518_big.jpg

五、Nginx反向代理FastDFS
1、fastdfs-nginx-module模块

① 安装配置fastdfs-nginx-module模块

//准备好的资源包进行解压
unzip fastdfs-nginx-module-master.zip
//复制配置修改配置
cp /usr/local/src/fastdfs-nginx-module-master/src/mod_fastdfs.conf /etc/fdfs/

② 修改内容
在这里插入图片描述


拷贝资源:复制 fastdfs 安装包中的两个配置文件 http.conf 和mime.types 到 /etc/fdfs 目录中

cp /usr/local/src/fastdfs-master/conf/http.conf /etc/fdfs/ 
cp /usr/local/src/fastdfs-master/conf/mime.types /etc/fdfs/

2、安装Nginx(配置)
# 下载 nginx 压缩包 
wget -P /usr/local/src http://nginx.org/download/nginx-1.18.0.tar.gz 
# 解压 
tar -zxvf /usr/local/src/nginx-1.18.0.tar.gz -C /usr/local/src
# 安装依赖
yum install -y gcc gcc-c++ pcre pcre-devel zlib zlib-devel openssl-devel
# 编译并安装
# 切换至 nginx 的解压目录
 cd /usr/local/src/nginx-1.18.0 
 # 创建 nginx 的安装目录 
 mkdir -p /usr/local/nginx 
 # 添加 fastdfs-nginx-module 模块,指定 nginx 的安装路径 
 ./configure --add-module=/usr/local/src/fastdfs-nginx-module-master/src -- prefix=/usr/local/nginx/ 
 # 编译并安装 
 make && make install

配置nginx加入以下内容:
在这里插入图片描述


启动nginx进行测试:: /usr/local/nginx/sbin/nginx
在这里插入图片描述

一定要关闭防火墙!!!


六、Java集成FastDFS(IDEA)

FastDFS 的作者余庆先生已经为我们开发好了 Java 对应的 SDK。这里需要解释一下:作者余庆并没有及时更新最新的 Java SDK 至 Maven 中央仓库,目前中央仓库最新版仍旧是 1.27 版。所以我们需要通过 Githubhttps://github.com/happyfish100/fastdfs-client-java 下载项目源码,再通过命令 mvn clean install 编译打包导入 Maven 本地仓库使用。
在这里插入图片描述

mvn clean compile install -Dmaven.test.skip=true

✧ 集成测试

创建一个普通的java项目
在这里插入图片描述


引入依赖:>>>

<dependencies>
    <!-- fastdfs java client -->
    <dependency>
      <groupId>org.csource</groupId>
      <artifactId>fastdfs-client-java</artifactId>
      <version>1.29-SNAPSHOT</version>
    </dependency>
    <!-- apache commons lang3 工具包 -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.11</version>
    </dependency>
    <!-- junit 单元测试 -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

FastDFSClient.java

package com.yjxxt.fdfs.util;
/**
 * FastDFS 分布式文件系统 Java 客户端工具类
 * 具体功能:文件上传、下载、替换、删除、查询文件元数据、查看文件详情
 */
public class FastDFSClient {

	// 获取配置文件地址
	private static final String CONF_FILENAME = Thread.currentThread()
			.getContextClassLoader().getResource("").getPath() + "fdfs_client.conf";
	// Storage 存储服务器客户端
	private static StorageClient storageClient = null;

	static {
		try {
			// 加载配置文件
			ClientGlobal.init(CONF_FILENAME);
			// 初始化 Tracker 客户端
			TrackerClient trackerClient = new TrackerClient(ClientGlobal.g_tracker_group);
			// 初始化 Tracker 服务端
			TrackerServer trackerServer = trackerClient.getTrackerServer();
			// 初始化 Storage 服务端
			StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
			// 初始化 Storage 客户端
			storageClient = new StorageClient(trackerServer, storageServer);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (MyException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 文件上传
	 *
	 * @param inputStream 上传的文件的字节输入流
	 * @param fileName    上传的文件的原始名
	 * @return
	 */
	public static String[] uploadFile(InputStream inputStream, String fileName) {
		try {
			// 准备字节数组
			byte[] fileBuff = null;
			// 文件元数据
			NameValuePair[] metaList = null;
			if (inputStream != null) {
				// 查看文件的长度
				int len = inputStream.available();
				// 初始化元数据数组
				metaList = new NameValuePair[2];
				// 第一组元数据,文件的原始名称
				metaList[0] = new NameValuePair("file_name", fileName);
				// 第二组元数据,文件的长度
				metaList[1] = new NameValuePair("file_length", String.valueOf(len));
				// 创建对应长度的字节数组
				fileBuff = new byte[len];
				// 将输入流中的字节内容,读到字节数组中
				inputStream.read(fileBuff);
			}
            /*
                上传文件。
                参数含义:要上传的文件的内容(使用字节数组传递),上传的文件的类型(扩展名),元数据
             */
			String[] fileids = storageClient.upload_file(fileBuff, getFileExt(fileName), metaList);
			return fileids;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (MyException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 文件上传
	 *
	 * @param file     上传的文件
	 * @param fileName 上传的文件的原始名
	 * @return
	 */
	public static String[] uploadFile(File file, String fileName) {
		try (FileInputStream fis = new FileInputStream(file)) {
			return uploadFile(fis, fileName);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获取文件后缀名(不带点)
	 *
	 * @param fileName
	 * @return 如:"jpg" or ""
	 */
	private static String getFileExt(String fileName) {
		if (StringUtils.isBlank(fileName) || !fileName.contains(".")) {
			return "";
		}
		return fileName.substring(fileName.lastIndexOf(".") + 1); // 不带最后的点
	}

	/**
	 * 获取文件详情
	 *
	 * @param groupName      组/卷名,默认值:group1
	 * @param remoteFileName 文件名,例如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
	 * @return 文件详情
	 */
	public static FileInfo getFileInfo(String groupName, String remoteFileName) {
		try {
			return storageClient.get_file_info(groupName == null ? "group1" : groupName, remoteFileName);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (MyException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获取元数据
	 *
	 * @param groupName      组/卷名,默认值:group1
	 * @param remoteFileName 文件名,例如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
	 * @return 文件的元数据数组
	 */
	public static NameValuePair[] getMetaData(String groupName, String remoteFileName) {
		try {
			// 根据组名和文件名通过 Storage 客户端获取文件的元数据数组
			return storageClient.get_metadata(groupName == null ? "group1" : groupName, remoteFileName);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (MyException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 文件下载
	 *
	 * @param groupName      组/卷名,默认值:group1
	 * @param remoteFileName 文件名,例如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
	 * @return 文件的字节输入流
	 */
	public static InputStream downloadFile(String groupName, String remoteFileName) {
		try {
			// 根据组名和文件名通过 Storage 客户端获取文件的字节数组
			byte[] bytes = storageClient.download_file(groupName == null ? "group1" : groupName, remoteFileName);
			// 返回字节流对象
			InputStream inputStream = new ByteArrayInputStream(bytes);
			return inputStream;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (MyException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 文件删除
	 *
	 * @param groupName      组/卷名,默认值:group1
	 * @param remoteFileName 文件名,例如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
	 * @return 0为成功,非0为失败
	 */
	public static int deleteFile(String groupName, String remoteFileName) {
		int result = -1;
		try {
			// 根据组名和文件名通过 Storage 客户端删除文件
			result = storageClient.delete_file(groupName == null ? "group1" : groupName, remoteFileName);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (MyException e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 修改一个已经存在的文件
	 *
	 * @param oldGroupName 旧组名
	 * @param oldFileName  旧文件名
	 * @param file         新文件
	 * @param fileName     新文件名
	 * @return
	 */
	public static String[] modifyFile(String oldGroupName, String oldFileName, File file, String fileName) {
		// 先上传
		String[] fileids = uploadFile(file, fileName);
		if (fileids == null) {
			return null;
		}
		// 再删除
		int delResult = deleteFile(oldGroupName, oldFileName);
		if (delResult != 0) {
			return null;
		}
		return fileids;
	}

}

fdfs_client.conf配置

# 超时时间
connect_timeout = 10
network_timeout = 30
# 编码字符集
charset = UTF-8
# tracker 服务器 HTTP 协议下暴露的端口
http.tracker_http_port = 8080
# tracker 服务器的 IP 和端口
tracker_server = 192.168.168.129:22122

测试类测试Fdfs.java

package com.yjxxt.fdfs.client;
public class Fdfs {
    // 文件上传
    @Test
    public void testUploadFile() {
        String[] fileids = FastDFSClient.uploadFile(new File("C:\\Users\\艾黎珂\\Desktop\\xxx.png"), "xxx.png");
        for (String fileid : fileids) {
            System.out.println("fileid = " + fileid);
        }
    }

    // 查看文件详情
    @Test
    public void testGetFileInfo() {
        FileInfo fileInfo = FastDFSClient.getFileInfo("group1", "M00/00/00/wKiogmGnZCyARV8-AAFzuvBwER8363.png");
        System.out.println("fileInfo = " + fileInfo);
    }

    // 获取文件数据
    @Test
    public void testGetMetaData() {
        NameValuePair[] metaDatas = FastDFSClient.getMetaData("group1", "M00/00/00/wKiogmGnZCyARV8-AAFzuvBwER8363.png");
        for (NameValuePair metaData : metaDatas) {
            System.out.println(metaData.getName() + "---" + metaData.getValue());
        }
    }

    // 文件下载
    @Test
    public void testDownloadFile() {
        InputStream is = FastDFSClient.downloadFile("group1", "M00/00/00/wKiogmGnZCyARV8-AAFzuvBwER8363.png");
        try (
                FileOutputStream fos = new FileOutputStream("C:\\Users\\艾黎珂\\Desktop\\wKgKZl9xMdiAcOLdAADhaCZ_RF0096.jpg"))
        {
            int len = 0;
            byte[] bytes = new byte[1024];
            while ((len = is.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
                fos.flush();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 文件删除
    @Test
    public void testDeleteFile() {
        int result = FastDFSClient.deleteFile("group1", "M00/00/00/wKiogmGnZCyARV8-AAFzuvBwER8363.png");
        System.out.println("result = " + result);
    }

    // 文件替换
    @Test
    public void testModifyFile() {
        String[] fileids = FastDFSClient.modifyFile("group1", "M00/00/00/wKiogmGnZCyARV8-AAFzuvBwER8363.png", new File("D:/mhw.jpg"), "mhw.jpg");
        for (String fileid : fileids) {
            System.out.println("fileid = " + fileid);
        }
    }
}

查看文件详情(其余结果不展示)
在这里插入图片描述


  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每日小新

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值