HDFS

HDFS入门

1.1.HDFS介绍

HDFS是Hadoop Distribute File System 的简称,意为:Hadoop分布式文件系统。是Hadoop核心组件之一,作为最底层的分布式存储服务而存在。

分布式:多个节点(一台主机一台服务器就是一个节点),每个节点的内容业务是不一样的
集群:多个节点,每个节点的内容业务是一样的

1.2.HDFS设计目标

1)  硬件故障是常态, HDFS将有成百上千的服务器组成,每一个组成部分都有可能出现故障。

_因此故障的检测和自动快速恢复是HDFS的核心架构目标。_

2)  HDFS上的应用与一般的应用不同,它们主要是以流式读取数据。HDFS被设计成适合批量处理,而不是用户交互式的。相较于数据访问的反应时间,更

_注重数据访问的高吞吐量。_

3)  典型的HDFS文件大小是GB到TB的级别。所以,HDFS被调整成支持大文件。它应该提供很高的聚合数据带宽,一个集群中支持数百个节点,一个集群中还应该支持千万级别的文件。

4)  大部分HDFS应用对文件要求的是
_write-one-read-many访问模式_

一个文件一旦创建、写入、关闭之后就不需要修改了。这一假设简化了数据一致性问题,使高吞吐量的数据访问成为可能。

5)  _移动计算的代价比之移动数据的代价低_。

一个应用请求的计算,离它操作的数据越近就越高效,这在数据达到海量级别的时候更是如此。将计算移动到数据附近,比之将数据移动到应用所在显然更好。

6)  _在异构的硬件和软件平台上的可移植性。_

这将推动需要大数据集的应用更广泛地采用HDFS作为平台。

2.HDFS重要特性

首先,它是一个文件系统,用于存储文件,通过统一的命名空间目录树来定位文件;
其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。

2.1.master/slave架构

一般一个HDFS集群是有一个Namenode和一定数目的Datanode组成。Namenode是HDFS集群主节点,Datanode是HDFS集群从节点,两种角色各司其职,共同协调完成分布式的文件存储服务。

2.2.分块存储

HDFS中的文件在物理上是分块存储(block)的,块的大小可以通过配置参数来规定,默认大小在hadoop2.x版本中是128M,1.X是64M
提高吞吐能力

2.3.名字空间(NameSpace)

HDFS支持传统的层次型文件组织结构。用户或者应用程序可以创建目录,然后将文件保存在这些目录里。文件系统名字空间的层次结构和大多数现有的文件系统类似:用户可以创建、删除、移动或重命名文件。
_Namenode负责维护文件系统的名字空间,任何对文件系统名字空间或属性的修改都将被Namenode记录下来。
HDFS会给客户端提供一个统一的抽象目录树,客户端通过路径来访问文件,形如:hdfs://namenode:port/dir-a/dir-b/dir-c/file.data。

2.4.Namenode元数据管理

我们把目录结构及文件分块位置信息叫做元数据。

_Namenode负责维护整个hdfs文件系统的目录树结构,以及每一个文件所对应的block块信息(block的id,及所在的datanode服务器)。

2.5.Datanode数据存储

_文件的各个block的具体存储管理由datanode节点承担。每一个block都可以在多个datanode上。Datanode需要定时向Namenode汇报自己持有的block信息。
存储多个副本(副本数量也可以通过参数设置dfs.replication,默认是3)。

2.6.副本机制

为容错,文件的所有block都会有副本。每个文件的block大小和副本系数都是可配置的。应用程序可以指定某个文件的副本数目。副本系数可以在文件创建的时候指定,也可以在之后改变。

简单谈机架感知
当存在HDFS集群节点非常多的时候
怎么备份呢==》存在机架感知==〉默认3副本机制
比如两台机架,上面有很多服务器
先判断客户端所在位置,也就是谁要上传文件,如果客户端在当前hdfs集群的一台上,那么第一个副本会放在和客户端同一个服务器上,不用网络传输,放在本地,第二个副本会在第二个机架上一台服务器上存放,第三个副本会在第二节点所在机架另一个服务器上存放。

如果客户端不在HDFS集群上,就近原则,通过交换机进行组装,也就是通过网络拓扑图看哪台机架离我最近,就去找一台,然后第二个副本继续找另一台备份。

2.7.一次写入,多次读出

HDFS是设计成适应一次写入,多次读出的场景,且不支持文件的修改。

正因为如此,HDFS适合用来做大数据分析的底层存储服务,并不适合用来做网盘等应用,因为修改不方便,延迟大,网络开销大,成本太高。

【_说一下namenode和seconderynamenode的作用?】
namenode主要负责维护文件系统的namespace和元数据(目录结构和文件分块位置信息), 以及每一个文件所对应的block块信息(block的id,及所在的datanode服务器)
secondarynamenode主要负责

3.HDFS基本操作

3.1.Shell 命令行客户端

Hadoop提供了文件系统的shell命令行客户端,使用方法如下:
hadoop fs
文件系统shell包括与Hadoop分布式文件系统(HDFS)以及Hadoop支持的其他文件系统(如本地FS,HFTP FS,S3 FS等)直接交互的各种类似shell的命令。所有FS shell命令都将路径URI作为参数。
URI格式为scheme://authority/path。对于HDFS,该scheme是hdfs,对于本地FS,该scheme是file。scheme和authority是可选的。如果未指定,则使用配置中指定的默认方案。
对于HDFS,命令示例如下:
hadoop fs -ls hdfs://namenode:host/parent/child
hadoop fs -ls /parent/child fs.defaultFS中有配置
对于本地文件系统,命令示例如下:
hadoop fs -ls file:///root/
如果使用的文件系统是HDFS,则使用hdfs dfs也是可以的,此时
hadoop fs = hdfs dfs

3.2.Shell 命令选项

选项名称
使用格式
含义
-ls
-ls <路径>
查看指定路径的当前目录结构
-lsr
-lsr <路径>
递归查看指定路径的目录结构
-du
-du <路径>
统计目录下个文件大小
-dus
-dus <路径>
汇总统计目录下文件(夹)大小
-count
-count [-q] <路径>
统计文件(夹)数量
-mv
-mv <源路径> <目的路径>
移动
-cp
-cp <源路径> <目的路径>
复制
-rm
-rm [-skipTrash] <路径>
删除文件/空白文件夹
-rmr
-rmr [-skipTrash] <路径>
递归删除
-put
-put <多个linux上的文件>
上传文件
-copyFromLocal
-copyFromLocal <多个linux上的文件>
从本地复制
-moveFromLocal
-moveFromLocal <多个linux上的文件>
从本地移动 (剪切)
-getmerge
-getmerge <源路径>
合并到本地
-cat
-cat
查看文件内容
-text
-text
查看文件内容
-copyToLocal
-copyToLocal [-ignoreCrc] [-crc] [hdfs源路径] [linux目的路径]
从本地复制
-moveToLocal
-moveToLocal [-crc]
从本地移动
-mkdir
-mkdir
创建空白文件夹
-setrep
-setrep [-R] [-w] <副本数> <路径>
修改副本数量
-touchz
-touchz <文件路径>
创建空白文件
-stat
-stat [format] <路径>
显示文件统计信息
-tail
-tail [-f] <文件>
查看文件尾部信息
-chmod
-chmod [-R] <权限模式> [路径]
修改权限
-chown
-chown [-R] [属主][:[属组]] 路径
修改属主
-chgrp
-chgrp [-R] 属组名称 路径
修改属组
-help
-help [命令选项]
帮助

3.3.Shell常用命令介绍

-ls
使用方法:hadoop fs -ls [-h]高级显示 [-R]递归
功能:显示文件、目录信息。
示例:hadoop fs -ls /user/hadoop/file1
-mkdir
使用方法:hadoop fs -mkdir [-p]
功能:在hdfs上创建目录,-p表示会创建路径中的各级父目录。
示例:hadoop fs -mkdir –p /user/hadoop/dir1
-put使用方法:hadoop fs -put [-f] [-p] [ -| .. ].
功能:将单个src或多个srcs从本地文件系统复制到目标文件系统。
-p:保留访问和修改时间,所有权和权限。
-f:覆盖目的地(如果已经存在)
示例:hadoop fs -put -f localfile1 localfile2 /user/hadoop/hadoopdir
-get
使用方法:hadoop fs -get [-ignorecrc] [-crc] [-p] [-f]
-ignorecrc:跳过对下载文件的CRC检查。
-crc:为下载的文件写CRC校验和。
功能:将文件复制到本地文件系统。
示例:hadoop fs -get hdfs://host:port/user/hadoop/file localfile
-appendToFile
使用方法:hadoop fs -appendToFile ...
功能:追加一个文件到已经存在的文件末尾
示例:hadoop fs -appendToFile localfile /hadoop/hadoopfile
-cat
使用方法:hadoop fs -cat [-ignoreCrc] URI [URI ...]
功能:显示文件内容到stdout
示例:hadoop fs -cat /hadoop/hadoopfile
-tail
使用方法:hadoop fs -tail [-f] URI
功能:将文件的最后一千字节内容显示到stdout。
-f选项将在文件增长时输出附加数据。
示例:hadoop fs -tail /hadoop/hadoopfile
-chgrp
使用方法:hadoop fs -chgrp [-R] GROUP URI [URI ...]
功能:更改文件组的关联。用户必须是文件的所有者,否则是超级用户。
-R将使改变在目录结构下递归进行。
示例:hadoop fs -chgrp othergroup /hadoop/hadoopfile
-chmod
功能:改变文件的权限。使用-R将使改变在目录结构下递归进行。
示例:hadoop fs -chmod 666 /hadoop/hadoopfile
-chown
功能:改变文件的拥有者。使用-R将使改变在目录结构下递归进行。
示例:hadoop fs -chown someuser:somegrp /hadoop/hadoopfile
-copyFromLocal
使用方法:hadoop fs -copyFromLocal URI
功能:从本地文件系统中拷贝文件到hdfs路径去
示例:hadoop fs -copyFromLocal /root/1.txt /
-copyToLocal
功能:从hdfs拷贝到本地
示例:hadoop fs -copyToLocal /aaa/jdk.tar.gz
-cp
功能:从hdfs的一个路径拷贝hdfs的另一个路径
示例: hadoop fs -cp /aaa/jdk.tar.gz /bbb/jdk.tar.gz.2
-mv
功能:在hdfs目录中移动文件
示例: hadoop fs -mv /aaa/jdk.tar.gz /
-getmerge
功能:合并下载多个文件
示例:比如hdfs的目录 /aaa/下有多个文件:log.1, log.2,log.3,...
hadoop fs -getmerge /aaa/log.* ./log.sum
-rm
功能:删除指定的文件。只删除非空目录和文件。-r 递归删除。
示例:hadoop fs -rm -r /aaa/bbb/
-df
功能:统计文件系统的可用空间信息
示例:hadoop fs -df -h /
-du
功能:显示目录中所有文件大小,当只指定一个文件时,显示此文件的大小。
示例:hadoop fs -du /user/hadoop/dir1
-setrep
功能:改变一个文件的副本系数。-R选项用于递归改变目录下所有文件的副本系数。
示例:hadoop fs -setrep -w 3 -R /user/hadoop/dir1

4.HDFS文件限额操作

hdfs文件的限额配置允许我们以文件大小或者文件个数来限制在某个目录下上传的文件数量或者文件内容总量,以便达到类似百度网盘网盘等限制每个用户允许上传的最大的文件的量。

4.1.数量限额

执行以下命令进行文件数量限额:
hadoop fs = hdfs dfs
创建hdfs文件夹
hadoop fs -mkdir -p /user/root/lisi
给该文件夹下面设置最多上传两个文件,上传文件,发现只能上传一个文件,默认本文件夹也是一个文件
hdfs dfsadmin -setQuota 2 lisi
_ 清除文件数量限制

hdfs dfsadmin -clrQuota /user/root/lisi

4.2.空间大小限额

执行以下命令进行空间大小限额
_ 限制空间大小4KB
hdfs dfsadmin -setSpaceQuota 4k /user/root/lisi

hdfs dfs -put /export/softwares/zookeeper-3.4.5-cdh5.14.0.tar.gz /user/root/lisi
上传超过4Kb的文件大小上去提示文件超过限额,因为默认为128M,加上备份block块是256M,所以限定必须是256M以上

_ 清除空间限额
hdfs dfsadmin -clrSpaceQuota /user/root/lisi
hdfs dfs -put /export/softwares/zookeeper-3.4.5-cdh5.14.0.tar.gz /user/root/lisi

4.3.查看hdfs文件限额数量

hdfs dfs -count -q -h /user/root/lisi

三.HDFS基本原理

###1.NameNode概述:
a.  NameNode是HDFS的核心。
b.  NameNode也称为Master。
c.  NameNode仅存储HDFS的元数据:文件系统中所有文件的目录树,并跟踪整个集群中的文件。
d.  NameNode不存储实际数据或数据集。数据本身实际存储在DataNodes中。
e.  NameNode知道HDFS中任何给定文件的块列表及其位置。使用此信息NameNode知道如何从块中构建文件。
f.  NameNode并_不持久化存储每个文件中各个块所在的DataNode的位置信息,这些信息会在系统启动时从数据节点重建。
g.  NameNode对于HDFS至关重要,_当NameNode关闭时_,HDFS / Hadoop集群无法访问。
h.  _NameNode是Hadoop集群中的单点故障。
i.  NameNode所在机器通常会配置有大量内存(RAM)。

NameNode有两块内容:
FileSystemImage:快照文件,放的元数据信息,如果namenode挂了,后期启动可以通过这个恢复。

Journal:Edithlogs 日志编辑文件,存储操作命令,主要指增删改
前者放硬盘,后者放内存

_Secondarynamenode:就是这两个文件的合并,将日志文件合并到快照文件中形成一个新的快照文件。


###2.DataNode概述
a. DataNode负责将实际数据存储在HDFS中。
b. DataNode也称为Slave。
c. NameNode和DataNode会保持不断通信。
d. DataNode启动时,它将自己发布到NameNode并汇报自己负责持有的块列表。
e. 当某个DataNode关闭时,它不会影响数据或群集的可用性。NameNode将安排由其他DataNode管理的块进行副本复制。
f. DataNode所在机器通常配置有大量的硬盘空间。因为实际数据存储在DataNode中。
g. DataNode会定期(dfs.heartbeat.interval配置项配置,默认是3秒)向NameNode发送心跳,如果NameNode长时间没有接受到DataNode发送的心跳, NameNode就会认为该DataNode失效。
h. block汇报时间间隔取参数dfs.blockreport.intervalMsec,参数未配置的话默认为6小时.

###3.HDFS的工作机制

NameNode负责管理整个文件系统元数据;DataNode负责管理具体文件数据块存储;_Secondary NameNode协助NameNode进行元数据的备份。

HDFS的内部工作机制对客户端保持透明,客户端请求访问HDFS都是通过向NameNode申请来进行。

####3.1.HDFS写数据流程

_详细步骤解析:
1. client发起文件上传请求,通过RPC与NameNode建立通讯,NameNode检查目标文件是否已存在,父目录是否存在,返回是否可以上传;
2. client请求第一个 block该传输到哪些DataNode服务器上;
3. NameNode根据配置文件中指定的备份数量及副本放置策略进行文件分配,返回可用的DataNode的地址,如:A,B,C;
注:默认存储策略由BlockPlacementPolicyDefault类支持。也就是日常生活中提到最经典的3副本策略。
1st replica 如果写请求方所在机器是其中一个datanode,则直接存放在本地,否则随机在集群中选择一个datanode.
2nd replica 第二个副本存放于不同第一个副本的所在的机架.
3rd replica 第三个副本存放于第二个副本所在的机架,但是属于不同的节点

4. client请求3台DataNode中的一台A上传数据(本质上是一个RPC调用,建立pipeline),A收到请求会继续调用B,然后B调用C,将整个pipeline建立完成,后逐级返回client;
5. client开始往A上传第一个block(先从磁盘读取数据放到一个本地内存缓存),以packet为单位(默认64K),A收到一个packet就会传给B,B传给C;A每传一个packet会放入一个应答队列等待应答。
6. 数据被分割成一个个packet数据包在pipeline上依次传输,在pipeline反方向上,逐个发送ack(命令正确应答),最终由pipeline中第一个DataNode节点A将pipeline ack发送给client;
7. 当一个block传输完成之后,client再次请求NameNode上传第二个block到服务器。

详细步骤图:

####3.2.HDFS读数据流程

_
详细步骤解析:
1. Client向NameNode发起RPC请求,来确定请求文件block所在的位置;
2. NameNode会视情况返回文件的部分或者全部block列表,对于每个block,NameNode都会返回含有该block副本的DataNode地址;
3. 这些返回的DN地址,会按照集群拓扑结构得出DataNode与客户端的距离,然后进行排序,排序两个规则:网络拓扑结构中距离Client近的排靠前;心跳机制中超时汇报的DN状态为STALE,这样的排靠后;
4. Client选取排序靠前的DataNode来读取block,如果客户端本身就是DataNode,那么将从本地直接获取数据;
5. 底层上本质是建立Socket Stream(FSDataInputStream),重复的调用父类DataInputStream的read方法,直到这个块上的数据读取完毕;
6. 当读完列表的block后,若文件读取还没有结束,客户端会继续向NameNode获取下一批的block列表;
7. 读取完一个block都会进行checksum验证,如果读取DataNode时出现错误,客户端会通知NameNode,然后再从下一个拥有该block副本的DataNode继续读。
8. read方法是并行的读取block信息,不是一块一块的读取;NameNode只是返回Client请求包含块的DataNode地址,并不是返回请求块的数据;
9. 最终读取来所有的block会合并成一个完整的最终文件。
详细步骤图:

四.HDFS的应用操作

1.HDFS的JAVA API操作

HDFS在生产应用中主要是客户端的开发,其核心步骤是从HDFS提供的api中构造一个HDFS的访问客户端对象,然后通过该客户端对象操作(增删改查)HDFS上的文件。
1.1.搭建开发环境
由于cdh版本的所有的软件涉及版权的问题,所以并没有将所有的jar包托管到maven仓库当中去,而是托管在了CDH自己的服务器上面,所以我们默认去maven的仓库下载不到,需要自己手动的添加repository去CDH仓库进行下载,以下两个地址是官方文档说明,请仔细查阅
https://www.cloudera.com/documentation/enterprise/release-notes/topics/cdh_vd_cdh5_maven_repo.html
https://www.cloudera.com/documentation/enterprise/release-notes/topics/cdh_vd_cdh5_maven_repo_514x.html

创建Maven工程,引入pom依赖


cloudera
https://repository.cloudera.com/artifactory/cloudera-repos/



org.apache.hadoop
hadoop-client
2.6.0-mr1-cdh5.14.0


org.apache.hadoop
hadoop-common
2.6.0-cdh5.14.0


org.apache.hadoop
hadoop-hdfs
2.6.0-cdh5.14.0


org.apache.hadoop
hadoop-mapreduce-client-core
2.6.0-cdh5.14.0


junit
junit
4.12

配置windows平台Hadoop环境
在windows上做HDFS客户端应用开发,需要设置Hadoop环境,而且要求是windows平台编译的Hadoop,不然会报以下的错误:
Failed to locate the winutils binary in the hadoop binary path java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries.

为此我们需要进行如下的操作:
A.在windows平台下编译Hadoop源码(可以参考资料编译,但不推荐)
B.使用已经编译好的Windows版本Hadoop
C.解压一份到windows的任意一个目录下
D.在windows系统中配置HADOOP_HOME指向你解压的安装包目录
E.在windows系统的path变量中加入HADOOP_HOME的bin目录

2.构造客户端对象

在java中操作HDFS,主要涉及以下Class:
Configuration:该类的对象封转了客户端或者服务器的配置;
FileSystem:该类的对象是一个文件系统对象,可以用该对象的一些方法来对文件进行操作,通过FileSystem的静态方法get获得该对象。
FileSystem fs = FileSystem.get(conf)
get方法从conf中的一个参数 fs.defaultFS的配置值判断具体是什么类型的文件系统。如果我们的代码中没有指定fs.defaultFS,并且工程classpath下也没有给定相应的配置,conf中的默认值就来自于hadoop的jar包中的core-default.xml,默认值为: file:///,则获取的将不是一个DistributedFileSystem的实例,而是一个本地文件系统的客户端对象。

3.示例代码

package cn.itcast.hdfs.stream;

import org.apache.commons.io.IOUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.*;

/*
* Stream流对接的方式上传下载数据
*
* */
public class StreamAccess {
//1,要操作先连接,首先创建客户端对象,null全局对象
FileSystem fs = null;

@Before
public  void createFS() throws IOException {
    //2, 那么如何得到对象呢,先看FileSystem是一个类还是接口control+H ,如果是接口找它的子类实现,
    //    可以通过DistributedFileSystem,点击FileSystem发现是抽象类:
    //    1,找它子类实现。
    //    2,找一个方法可以返回当前类的对象static FileSystem get(Configuration conf);newInstance
    //需要一个conf那就给一个conf配置,创建文件系统,new一个配置对象,去添加东西
    //如果什么参数都不给,那么创建都是本地文件系统file:///

    Configuration conf = new Configuration();


    //那么怎么创建hdfs呢==》50070,9000,用set(K,V)(String name,String value)
    //core.site里用于设置Hadoop的文件系统,由URI指定
    //优先级       当前的配置 > 项目的classpath > 用户设置的Hadoop/etc/hadoop/core-default.xml > 默认的core-default.xml

    conf.set("fs.defaultFS","hdfs://hadoop01:8020");

// System.setProperty("HADOOP_USER_NAME", "root");将当前系统变成root去访问

    fs = FileSystem.get(conf);

// 第二种方式get(new URI(Str:"hdfs://hadoop01:8020"),Configuration conf)//可以加user:"root"
// 第三种方式newInstance(Configuration conf);
// 第四种方式newInstance(new URI(Str:"hdfs://hadoop01:8020")Configuration conf)

// 第二种方式使用最多,而使用set更加灵活set副本个数或者块大小("dfs.replication","2");("dfs.block.size","64m");

//那么get和newInstance有什么区别==>看源码可知,
// get有一个判断,如果去获取的时候判断没有文件系统的话,
// 它会进行一个创建动作,如果有就直接使用
}

@Test
public void name() {

        System.out.println(fs);
}

//上传
@Test
public void copyFromLocalFile() throws IOException {

// fs可以调用方法,使用shell命令的封装
fs.copyFromLocalFile(true,true,new Path("/Users/vincent/Desktop/a.txt"),new Path("hdfs://hadoop01:8020/Test/"));
}

//上传
@Test
public  void streamToHdfs() throws IOException {
    //open可以打开一个输入流,create打开输出流,
    //1,得到本地的输入流,
    FileInputStream fileInputStream = new FileInputStream(new File("/Users/vincent/Desktop/b.txt"));
    // 2。得到hdfs的输出流,要写一个文件
    FSDataOutputStream fsDataOutputStream = fs.create(new Path("/Test/h.txt"));


    //3。流对接commons.io   copy(InputStream input,Writer output)

// IOUtils.copy(fileInputStream, fsDataOutputStream);
IOUtils.copy(fileInputStream,fsDataOutputStream);
}

//下载
@Test
public void streamDownLoad() throws IOException {
    //hdfs得到一个fsDataOutputStream对象的的一个输入流,用open
    FSDataInputStream inputStream = fs.open(new Path("/Test/h.txt"));
    //得到本地的输出流
    FileOutputStream fileOutputStream = new FileOutputStream(new File("/Users/vincent/Desktop/c.txt"));
   /*
    * inputStream.seek(345);
    * 通过偏移量去暂缓下载
    * */
    IOUtils.copy(inputStream,fileOutputStream);
}

//获取HDFS上所有文件及路径 Hadoop fs -ls -r
@Test
public void findAllFiles() throws IOException {
    RemoteIterator Iterator = fs.listFiles(new Path(""), true);
    //源码最后得到的也是FileStatus,返回的是一个迭代器,这里遍历
    while (Iterator.hasNext()) {
        LocatedFileStatus fileStatus = Iterator.next();//得到一个文件状态fileStatus是对所有文件文件夹的所属数据信息

        System.out.println(fileStatus.getPath()); //路径);
       /*

        fileStatus.getBlockLocations();//bl块信息
        fileStatus.getAccessTime();//访问时间
        fileStatus.getBlockSize();//bl块大小
        fileStatus.getGroup();//属于哪个组
        fileStatus.getOwner();//属于哪个用户

        * len长度
        * modification修改时间
        * premission权限
        * replication副本
        * */
    }

}

//遍历hdfs上所有的文件和文件夹
@Test
public void findAll() throws IOException {
    findFile("/");
}

private void findFile(String path) throws IOException {
    FileStatus[] fileStatuses = fs.listStatus(new Path(path));
    for (FileStatus fileStatus : fileStatuses) {
        //每一个文件或文件夹的所有数据
        if (fs.isDirectory(fileStatus.getPath())){
            //得到的就是文件夹
            System.out.println("d------" + fileStatus.getPath());
            findFile(fileStatus.getPath().toString());

        }else{
            //得到文件
            System.out.println("f------" + fileStatus.getPath());
        }
    }
}

//获取指定block块信息
@Test
public void Catblock() throws IOException {
    //如果path()指定的是一个文件,那么数组返回的大小就是1
    FileStatus[] fileStatuses = fs.listStatus(new Path("/Test/h.txt"));
    FileStatus fileStatus = fileStatuses[0];

    //返回当前文件所有block块的地址列表
    BlockLocation[] fileBlockLocations = fs.getFileBlockLocations(fileStatus, 0, fileStatus.getLen());

    for (BlockLocation fileBlockLocation : fileBlockLocations) {

        //得到每个块信息
        System.out.println(fileBlockLocation.getLength() + "offset" + fileBlockLocation.getOffset());

        //得到当前块在哪个节点host上
        String[] hosts = fileBlockLocation.getHosts();
        for (String host : hosts) {
            System.out.println(host);
        }
        }
    }

    @Test
    //获取指定block块信息
    public void catBlocks() throws IOException {
          //得到hdfs的输入流
        FSDataInputStream open = fs.open(new Path("/Test"));

        //如果path()指定的是一个文件,那么数组返回的大小就是1
        FileStatus[] fileStatuses = fs.listStatus(new Path("/Test/h.txt"));
        FileStatus fileStatus = fileStatuses[0];

        //返回当前文件所有block块的地址列表
        BlockLocation[] fileBlockLocations = fs.getFileBlockLocations(fileStatus, 0, fileStatus.getLen());

        //我只要第二个块的长度和起始
        long length = fileBlockLocations[1].getLength();
        long offset = fileBlockLocations[1].getOffset();


    }



@After
public void close() throws IOException {

// 如果是get方式不建议关闭,如果是newInstance,建议关闭,
// 因为newInstance是新创建的对象,而get可能是别人获取到到
fs.close();
}
}

你可能感兴趣的:(HDFS)