1. 文件元数据:FileStatus
任何文件系统的一个重要特征都是提供其目录结构浏览和检索它所存文件和目录相关信息的功能。FileStatus类封装了文件系统中文件和目录的元数据,包括文件长度、块大小、复本、修改时间、所有者、权限信息。
FileStatus的getFileStatus()方法用于获取文件或目录的FileStatus对象。看下面:
范例1,展示文件状态信息
public class ShowFileStatusTest{
private MiniDFSCluster cluster;//use an in-process cluster for testing
private FileSystem fs;
@Before
public void setup() throws IOException{
Configuration conf = new Configuration();
if(System.getProperty("test.build.data") == null){
System.setProperty("test.build.data","/tmp");
}
cluster = new MiniDFSCluster.Builder(conf).build();
fs = cluster.getFileSystem();
OutputStream out = fs.create(new Path("/dir/file"));
out.write("content".getBytes("UTF-8"));
out.close();
}
@After
public void tearDown() throws IOException{
if(fs != null){
fs.close();
}
if(cluster != null){
cluster.shutdown();
}
}
@Test
public void throwsFileNotFoundForNonExistentFile() throws IOException{
fs.getFileStatus(new Path("no-such-file"));
}
@Test
public void fileStatusForFile() throws IOException{
Path file = new Path("/dir/file");
FileStatus stat = fs.getFileStatus(file);
assertThat(stat.getPath().toUri().getPath(),is("/dir/file"));
assertThat(stat.isDirectory(),is(false));
assertThat(stat.getLen(),is(7L));
assertThat(stat.getModificationTime(),is(lessThanOrEqualTo(System.CurrentTimeMillis())));
assertThat(stat.getReplication(),is((short)1));
assertThat(stat.getBlockSize(),is(128*1024*1024L));
assertThat(stat.getOwner(),is(System.getProperty("user.name")));
assertThat(stat.getGroup(),is("supergroup"));
assertThat(stat.getPermission().toString(),is("rw-r--r--"));
}
@Test
public void fileStatusForDirectory() throws IOException{
Path dir = new Path("/dir");
FileStatus stat = fs.getFileStatus(dir);
assertThat(stat.getPath().toUri().getPath(),is("/dir"));
assertThat(stat.isDirectory(),is(true));
assertThat(stat.getLen(),is(0L));
assertThat(stat.getModificationTime(),is(lessThanOrEqualTo(System.CurrentTimeMillis())));
assertThat(stat.getReplication(),is((short)0));
assertThat(stat.getBlockSize(),is(0L));
assertThat(stat.getOwner(),is(System.getProperty("user.name")));
assertThat(stat.getGroup(),is("supergroup"));
assertThat(stat.getPermission().toString(),is("rwxr-xr-x"));
}
}
如果文件或目录均不存在,会抛出一个FileNotFoundException异常。但如果只是想检查文件或目录是否存在,那么调用exists()方法会更方便:public boolean exists(Path f) throws IOException
2. 列出文件
查找一个文件或目录相关的信息很实用,但通常还需要能够列出目录中的内容。
以下就是FileSystem的listStatus()方法的功能:
public FileStatus[] listStatus(Path f) throws IOException
public FileStatus[] listStatus(Path f, PathFilter filter) throws IOException
public FileStatus[] listStatus(Path[] files) throws IOException
public FileStatus[] listStatus(Path[] files, PathFilter filter) throws IOException
当传入的参数是一个文件时,它会简单转变为以数组方式返回长度为1的FileStatus对象。
当传入的参数是一个目录时,则返回0或者多个FileStatus对象,表示此目录中包含的文件和目录。
它的重载方法允许使用PathFilter来限制匹配的文件和目录。如果指定一组路径,其执行结果相当于依次轮流传递每条路径并对其调用listStatus()方法,再将FileStatus对象数组累积存入同一数组中,该方法更方便。在文件系统树的不同分支构建输入文件列表时,这是很有用的。举个例子(范例2),Hadoop中的FileUtil中stat2Path()方法的使用,它将一个FileStatus对象数组转换为一个Path对象数组:
范例2:
范例2:显示Hadoop文件系统中一组路径的文件信息
public class ListStatus{
public static void main(String[] args) throws Exception{
String uri = arg[0];
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(uri),conf);
Path[] paths = new Path(args.length);
for(int i = 0; i < paths.length; i++){
path[i] = new Path(args[i]);
}
FileStatus[] status = fs.listStatus(paths);
Path[] listedPaths = FileUtil.stat2Paths(status);
for(Path p : listedPaths){
System.out.println(p);
}
}
}
程序运行示例结果:
% hadoop ListStatus hdfs://localhost/ hdfs://localhost/user/tom
hdfs://localhost/user
hdfs://localhost/user/tom/books
hdfs://localhost/user/tom/quangle.txt
3. 文件模式——通配符
在单个操作中处理一批文件是一个很常见的需求。例如,一个用于处理日志的MapReduce作业可能需要分析一个月包含在大量目录中的日志文件。在一个表达式中使用通配符来匹配多个文件是比较方便的,无需列举每个文件和目录来指定输入,该操作称为“通配”,Hadoop为执行通配提供了两个FileSystem方法。
public FileStatus[] golbStatus(Path pathPattern) throws IOException
public FileStatus[] globStatus(Path pathPattern,PathFilter filter) throws IOException
globStatus()方法返回路径格式与指定模式匹配的所有FileStatus对象组成的数组,并按路径排序。PathFilter命令作为可选项可以进一步对匹配结果进行限制。
Hadoop支持的通配符与Unix bash shell支持的相同,见下表
通配符 | 名称 | 匹配 |
---|---|---|
* | 星号 | 匹配 0 或多个字符 |
? | 问号 | 匹配单一字符 |
[ab] | 字符类 | 匹配{a,b}集合中的一个字符 |
[^ab] | 非字符类 | 匹配非{a,b}集合中的一个字符 |
[a-b] | 字符范围 | 匹配一个在{a,b}范围内的字符,包括a和b,a在字典顺序上要小于或等于b |
[^a-b] | 非字符范围 | 匹配一个不在{a,b}范围内的字符,包括a和b,a在字典顺序上要小于或等于b |
{a,b} | 或选择 | 匹配包含 a 或 b 中的一个表达式 |
\c | 转义字符 | 匹配元字符c |
假设有日志文件存储在按日期分层组织的目录结构中,那么2019年的最后一天的日志文件就会保存在名为/2019/12/31的目录中。
下表是常用文件通配符的示例
通配符 | 扩展 |
---|---|
/* | /2018/2019 |
/*/* | /2018/12/2019/01 |
/*/12/* | /2018/12/01/2018/12/02 |
/201? | /2017/2018 |
/201[78] | /2017/2018 |
/201[7-8] | /2017/2018 |
/201[^01234569] | /2017/2018 |
/*/*/{31,01} | /2017/12/31/2017/01/01 |
/*/*/3{0,1} | /2017/12/30/2017/12/31 |
/*/{12/31,01/01} | /2017/12/31/2018/01/01 |
4. PathFilter对象——过滤文件
通配符模式并不总能够精确描述我们想要访问的文件集。比如,使用通配格式排除一个特定的文件就不太可能。FileSystem中的listStatus()和globStatus()方法提供了可选的PathFilter对象,以编程方式控制通配符。
package org.apache.hadoop.fs;
public insterface PathFilter{
boolean accept(Path path);
}
PathFilter与java.io.FileFilter一样,是Path对象而不是File对象。
下面举个例子(范例3),用PathFilter来排除匹配正则表达式的路径。
public class RegexExcludePathFilter implements PathFilter{
private final String regex;
public RegexExcludePathFilter(String regex){
this.regex = regex;
}
public boolean accept(Path path){
return !path.toString().matches(regex);
}
}
这个过滤器只传递不匹配于正则表达式的文件。在通配符中选出一组需要包含的初始文件之后,过滤器可优化其结果。如下示例将扩展到/2017/12/30:
fs.globStatus(new Path("/2017/*/*"),new RegexExcluderFilter("^.*/2017/12/31$"))
以Path为代表,过滤器只能作用于文件名。不能针对文件的属性(例如创建时间)来构建过滤器。但是,过滤器却能实现通配符模式和正则表达式都无法完成的匹配任务。例如,如果将文件存储在按照日期排列的目录结构中,则可以写一个PathFilter选出给定时间范围内的文件。