FastDFS 文件 (上传,下载,删除)
本文是上一篇文章的后续,详情点击该链接~
简介
FastDFS是一个轻量级的开源分布式文件系统。自2008年4月份开始启动。类似google FS的一个轻量级分布式文件系统,C语言实现,支持Linux、FreeBSD、AIX等UNIX系统。主要解决了大容量的文件存储和高并发访问的问题,文件存取时实现了负载均衡。实现了软件方式的磁盘阵列(Redundant Arrays of Independent Drives,RAID),可以使用廉价的IDE(Integrated Drive Electronics)硬盘进行存储。并且支持存储服务器在线扩容。支持相同内容的文件只保存一份,节约磁盘空间。
FastDFS没有官网。但是作者余庆(happy_fish100)担任chinaunix中FastDFS板块版主。并且会不定期更新板块中内容。
如果你要下载的话,就点击这里
安装点这里
关于角色问题
FastDFS通常有三种角色。他们分别是 Client:客户端、Tracker Server:跟踪服务器、Storage Server:存储服务器
Client客户端的话,它主要是Java语言编写的项目
Tracker Server跟踪服务器,就主要做调度工作,在访问上起负载均衡的作用。在内存中记录集群中group和storage server的状态信息,是连接Client和Storage server的枢纽。
而Storage Server存储服务器,主要就是把文件和文件属性(meta data)都保存到存储服务器上
架构分析
当只有两个角色: tracker server和storage server的时候,则不需要存储文件索引信息。
在FastDFS中所有服务器都是对等的,不存在Master-Slave关系。
它们存储服务器通常采用分组方式,同组内存储服务器上的文件完全相同(RAID 1)
而不同组的storage server之间不会相互通信。由storage server主动向tracker server报告状态信息,tracker server之间不会相互通信。
文件上传
添加依赖
<dependencies>
<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>
</dependencies>
配置文件 (fastDFS.properties)
# 链接超时
fastdfs.connect_timeout_in_seconds=10
# 工作超时
fastdfs.network_timeout_in_seconds=30
# 字符集
fastdfs.charset=UTF-8
# tracker服务器地址,多个地址使用逗号','分隔。
fastdfs.tracker_servers=192.168.147.128:22122
# 和配合文件/etc/fdfs/tracker.conf中的http.server_port配置完全相同
fastdfs.http_tracker_http_port=8080
代码实现
注意:运行之前记得先启动服务
参考命令: service fdfs_trackerd start 、 service fdfs_storaged start
检查防火墙状态: service firewalld status
如果没关闭,就最好关闭防火墙: service firewalld stop
public class Test {
//文件路径
private static String dirPath = "C:\\Users\\36961\\Desktop\\desk_picture\\";
public static void main(String[] args) throws IOException, MyException {
upload();
}
//上传
public static void upload() throws IOException, MyException {
//加载配置文件,内容是tracker的ip和端口。还有链接的设置,如:超时
//fastDFS的Java客户端提供了两种文件配置的格式:一种是conf,一种是properties
Properties properties = new Properties();
properties.load(Test.class.getClassLoader().getResourceAsStream("fastDFS.properties"));
ClientGlobal.initByProperties(properties);
//初始化客户端对象 StorageClient,存储客户端
TrackerClient tracker = new TrackerClient();
TrackerServer server = tracker.getConnection();
StorageServer storage = tracker.getStoreStorage(server);
StorageClient storageClient = new StorageClient(server,storage);
//文件上传
//读取文件字节内容到一个字节数组中,一个文件必须完整的存在一个数组中
FileInputStream fileInputStream = new FileInputStream(dirPath + "time.png");
//根据文件的字节长度.创建一个等长的数组
byte[]buf = new byte[fileInputStream.available()];
//把文件的内容完整的读取到数组中
fileInputStream.read(buf,0,buf.length);
//元数据,有使用者自定义,是名值对形式存在的数组。如文件名=time.png,文件长度=6666等
NameValuePair[] valuePair = new NameValuePair[]{
new NameValuePair("name","time.png")
};
//上传文件.返回一个字符串数组长度为2。0下标代表组名,1下标代表文件路径和文件名
String[]result = storageClient.upload_file(buf,".png",valuePair);
//处理上传结果
System.out.println("result[0] = " + result[0]);
System.out.println("result[1] = " + result[1]);
}
}
刚才的那种写法是比较原始的,复用性不高。所以我们肯定是要封装一个工具类的
代码修改
FastDfsUtil(工具)
public class FastDfsUtil {
private FastDfsUtil(){}
private static final Properties properties = new Properties();
private static StorageClient storageClient;
static{
try {
properties.load(Test.class.getClassLoader().getResourceAsStream("fastDFS.properties"));
ClientGlobal.initByProperties(properties);
// 初始化客户端对象。 StorageClient,存储客户端。
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
storageClient = new StorageClient(trackerServer, storageServer);
}catch (Exception e){
e.printStackTrace();
// 初始化代码块异常,抛出错误,停止虚拟机。
throw new ExceptionInInitializerError(e);
}
}
// 上传文件
public static String uploadFile(byte[] datas, String extName, NameValuePair[] nvps){
try {
String[] result = storageClient.upload_file(datas, extName, nvps);
String path = result[0] + "/" + result[1];
return path;
}catch (Exception e){
e.printStackTrace();
// 上传失败
return null;
}
}
}
Test类
public class Test {
//文件路径
private static String dirPath = "C:\\Users\\36961\\Desktop\\desk_picture\\";
public static void main(String[] args) throws IOException, MyException {
upload();
}
//上传
public static void upload() throws IOException, MyException {
FileInputStream in = new FileInputStream(dirPath+"time.png");
int length = in.available();
byte[] datas = new byte[length];
in.read(datas, 0, length);
NameValuePair[] nvps = new NameValuePair[]{
new NameValuePair("name", "time.png"),
new NameValuePair("length", length+"")
};
String result = FastDfsUtil.uploadFile(datas, "png", nvps);
System.out.println(result);
}
}
这样一来就方便多了~
查看上传的文件(本人刚才操作的时候上传了三次。。。)
参考命令: cd /usr/local/fastdfs/storage/store/data/00/00 (以具体路径为准)
文件下载
修改FastDfsUtil工具类
//下载文件
public static byte[] download(String group, String path){
try {
return storageClient.download_file(group, path);
}catch(Exception e){
e.printStackTrace();
// 下载失败
return null;
}
}
// 根据组名和文件信息查询元数据数组
public static NameValuePair[] getMetaDatas(String group, String path){
try {
return storageClient.get_metadata(group, path);
}catch (Exception e){
e.printStackTrace();
// 获取元数据失败
return null;
}
}
Test类
public static void download() throws IOException {
String grop = "group1";
//这个文件名是刚刚上传的时候,通过Println打印的结果而得到的
//如果想灵活一点,可以考虑使用变量来存储返回结果
//主要是为了更好理解才这么写
String path = "M00/00/00/wKiTgF8rseGAI7kMAAQPXweodas397.png";
NameValuePair[] nameValuePair = FastDfsUtil.getMetaDatas(grop,path);
byte[]buf = FastDfsUtil.download(grop,path);
String localFileName = "";
for(NameValuePair nvp : nameValuePair){
if(nvp.getName().equals("name")){
// 文件命名
localFileName = nvp.getValue();
}
}
if("".equals(localFileName)){
// 元数据中没有文件名,设置文件名为随机命名
localFileName = UUID.randomUUID().toString() + path.substring(path.lastIndexOf(".")+1);
}
FileOutputStream out = new FileOutputStream("D:\\"+localFileName);
// 存储文件
out.write(buf, 0, buf.length);
out.flush(); // 可以省略,关闭流的时候,自动刷新缓冲区。
out.close();
}
文件删除
FastDfsUtil工具类
/**
* 删除文件。注意,删除FastDFS管理的文件,一定要使用客户端API完成。
* 直接在XShell中使用rm命令删除文件,会破坏FastDFS对文件的管理逻辑。
* 因为,通过客户端API删除的文件,Storage服务器会上报信息给Tracker。
* 直接rm删除文件,没有上报过程,再下载或查看文件的时候,会有资源找不到错误。
*/
public static boolean delete(String group, String path){
try {
int stat = storageClient.delete_file(group, path);
return stat == 0;
}catch (Exception e){
e.printStackTrace();
// 删除失败
return false;
}
}
Test类
//删除
public static void delete() throws Exception {
String group = "group1";
String path = "M00/00/00/wKiTgF8rseGAI7kMAAQPXweodas397.png";
boolean flag = FastDfsUtil.delete(group, path);
System.out.println(flag ? "删除成功" : "删除失败");
}