1. 文件元数据:FileStatus
任何文件系统的一个重要特征都是提供其目录结构浏览和检索它所存文件和目录相关信息的功能。FileStatus类封装了文件系统中文件和目录的元数据,包括文件长度、块大小、复本、修改时间、所有者以及权限信息。
FileSystem的getFileStatus()方法用于获取文件或目录的FileStatus对象。范例3-5显示了它的用法。
范例3-5. 展示文件状态信息
public class ShowFileStatusTest { private MiniDFSCluster cluster; // use an in-process HDFS 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(conf, 1, true, null); 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(expected = FileNotFoundException.class) 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.isDir(), is(false)); assertThat(stat.getLen(), is(7L)); assertThat(stat.getModificationTime(), is(lessThanOrEqualTo(System.currentTimeMillis()))); assertThat(stat.getReplication(), is((short) 1)); assertThat(stat.getBlockSize(), is(64 * 1024 * 1024L)); assertThat(stat.getOwner(), is("tom")); 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.isDir(), 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("tom")); 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来限制匹配的文件和目录——可以参见3.5.5节提供的例子。最后,如果指定一组路径,其执行结果相当于依次轮流传递每条路径并对其调用listStatus()方法,再将FileStatus对象数组累积存入同一数组中,但该方法更为方便。这从文件系统树的不同分支构建输入文件列表时,这是很有用的。范例3-6简单显示了这个方法。注意FileUtil中stat2Paths()方法的使用,它将一个FileStatus对象数组转换为一个Path对象数组。
范例3-6. 显示Hadoop 文件系统中一组路径的文件信息
public class ListStatus { public static void main(String[] args) throws Exception { String uri = args[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++) { paths[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作业可能需要分析一个月内包含在大量目录中的日志文件。在一个表达式中使用通配符来匹配多个文件是比较方便的,无需列举每个文件和目录来指定输入,该操作称为“通配”(globbing)。Hadoop为执行通配提供了两个FileSystem方法:
public FileStatus[] globStatus(Path pathPattern) throws IOException public FileStatus[] globStatus(Path pathPattern, PathFilter filter) throws IOException
globStatus()方法返回与其路径匹配于指定模式的所有文件的FileStatus对象数组,并按路径排序。PathFilter命令作为可选项可以进一步对匹配结果进行限制。
Hadoop支持的通配符与Unix bash的相同(参见表3-2)。
表3-2. 通配符及其含义
通配符 |
名称 |
匹配 |
* |
星号 |
匹配0 或多个字符 |
? |
问号 |
匹配单一字符 |
[ab] |
字符类 |
匹配{a,b}集合中的一个字符 |
[^ab] |
非字符类 |
匹配非{a,b}集合中的一个字符 |
[a-b] |
字符范围 |
匹配一个在{a,b}范围内的字符(包括ab),a在字典顺序上要小于或等于b |
[^a-b] |
非字符范围 |
匹配一个不在{a,b}范围内的字符(包括ab),a在字典顺序上要小于或等于b |
{a,b} |
或选择 |
匹配包含a 或b 中的一个的表达式 |
\c |
转义字符 |
匹配元字符c |
假设有日志文件存储在按日期分层组织的目录结构中。如此一来,2007年最后一天的日志文件就会保存在名为/2007/12/31的目录中。假设整个文件列表如下所示:
/ |—— 2007/ | ┗——12/ | |—— 30/ | ┗——31/ ┗——2008/ ┗—— 01/ |—— 01/ ┗—— 02/
一些文件通配符及其扩展如下所示。
通配符 |
扩展 |
/* |
/2007/2008 |
/*/* |
/2007/12/2008/01 |
/*/12/* |
/2007/12/30/2007/12/31 |
/200? |
/2007/2008 |
/200[78] |
/2007/2008 |
/200[7-8] |
/2007/2008 |
/200[^01234569] |
/2007/2008 |
/*/*/{31,01} |
/2007/12/31/2008/01/01 |
/*/*/3{0,1} |
/2007/12/30/2007/12/31 |
/*/{12/31,01/01} |
/2007/12/31/2008/01/01 |
4. PathFilter对象
通配符模式并不总能够精确地描述我们想要访问的文件集。比如,使用通配格式排除一个特定的文件就不太可能。FileSystem中的listStatus()和globStatus()方法提供了可选的PathFilter对象,以编程方式控制通配符:
package org.apache.hadoop.fs; public interface PathFilter { boolean accept(Path path); }
PathFilter与java.io.FileFilter一样,是Path对象而不是File对象。
范例3-7显示了PathFilter用于排除匹配正则表达式的路径。
范例3-7. 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); } }
这个过滤器只传递不匹配于正则表达式的文件。在通配符选出一组需要包含的初始文件之后,过滤器可优化其结果。如下示例将扩展到/2007/12/30:
fs.globStatus(new Path("/2007/*/*"), new RegexExcludeFilter("^.*/2007/12/31$"))
过滤器由Path表示,只能作用于文件名。不能针对文件的属性(例如创建时间)来构建过滤器。但是,通配符模式和正则表达式同样无法对文件属性进行匹配。例如,如果将文件存储在按照日期排列的目录结构中(如前一节中讲述的那样),则可以根据Pathfilter在给定时间范围内选出文件。