一提到hadoop文件系统,通常想到的就是HDFS,即Hadoop Distributed File System,但除了HDFS外,hadoop还支持其它类型的文件系统,比如Amazon S3、Swift文件系统等,而这些文件系统都扩展自抽象基础类FileSystem,该类提供了丰富的方法用于对文件系统进行操作,比如创建目录、删除文件、重命名等。无论使用的是HDFS还是Swift文件系统,或者其它所支持的文件系统,推荐在应用程序使用FileSystem对象引用实际的文件系统,比如FileSystem local= FileSystem.getLocal(Configuration conf),该语句返回LocalFileSystem。FileSystem的类层次关系如下图所示,其中将重点学习红色标记的DistributedFileSystem,其它文件系统只进行简要的概述。
FTPFileSystem,由Apache Commons Net提供的由FTP客户端支持的文件系统。S3FileSystem,由Amazon S3支持的基于block的文件系统。NativeS3FileSystem,用于读写存储在Amazon S3中文件的文件系统,与S3FileSystem不同的是,该实现将文件按照原始格式存储在S3,这样其它S3工具可以读取文件。RawLocalFileSystem,原始的本地文件系统,而继承自ChecksumFileSystem的LocalFileSystem则为要计算校验和的文件系统。ChecksumFileSystem实现了一个客户端挂载表,该类的规则和实现和ViewFs是相同的,详细内容可以参考《Hadoop-2.4.1学习之ViewFs》。HarFileSystem,Hadoop归档文件系统,该归档文件系统有形如_index*的索引文件和形如part-*的内容文件。HttpFSFileSystem允许用户通过基于HTTP的HttpFSServer访问HDFS。HftpFileSystem为基于HTTP访问文件系统的实现。WebHdfsFileSystem为HDFS在web上的文件系统。SwiftNativeFileSystem为Swift(Openstack对象存储)文件系统的实现。最后一个也是最常用的文件系统为DistributedFileSystem,该类实现了DFS系统,通过该类用户代码与HDFS交互。
在简要概述了FileSystem及其子类后,回过头再详细学习一下该类的重要方法。由于该类为抽象类,故没有办法直接创建该类的对象,但该类提供了静态方法用于创建对象,分别为:
方法 |
描述 |
get(Configuration conf) |
根据conf获取具体的文件系统对象 |
get(URI uri, Configuration conf) |
基于uri和conf创建文件系统对象 |
get(URI uri, Configuration conf, String user) |
基于uri,conf和user获取文件系统 |
getLocal(Configuration conf) |
获取本地文件系统 |
newInstance(Configuration conf) |
返回唯一的文件系统对象,该方法总是返回新的对象 |
newInstance(URI uri, Configuration conf) |
基于uri返回新的文件系统对象 |
newInstance(URI uri, Configuration conf, String user) |
基于uri,conf和user获取文件系统 |
newInstanceLocal(Configuration conf) |
返回新的本地文件系统对象 |
同样为获取文件系统对象的方法,get和newInstance有什么不同呢?从描述可知,newInstance总是返回新的文件系统对象,那get呢?要想了解其中的具体区别,最好的方法莫过于查看源代码了,首先查看get(URI uri, Configuration conf)的源代码:
public static FileSystem get(URI uri, Configuration conf) throws IOException { String scheme = uri.getScheme(); String authority = uri.getAuthority(); if (scheme == null && authority == null) {// use default FS //根据fs.defaultFS的值获取文件系统,若未设置该参数则根据file:///返回文件系统 return get(conf); } if (scheme != null && authority == null) {// no authority //根据fs.defaultFS的值创建URI,若未设置则使用file:///创建URI URI defaultUri = getDefaultUri(conf); if (scheme.equals(defaultUri.getScheme()) // if scheme matches default && defaultUri.getAuthority() != null) { // & default has authority return get(defaultUri, conf); // return default } } String disableCacheName = String.format("fs.%s.impl.disable.cache", scheme); if (conf.getBoolean(disableCacheName, false)) { //根据uri和conf创建FileSystem return createFileSystem(uri, conf); } //若未设置缓存参数为true,则默认从CACHE中获取文件系统对象 return CACHE.get(uri, conf); }
从上面的代码可以得知,get方法不是每次都创建FileSystem对象,会从缓存中获取FileSystem对象,而newInstance方法则会每次都创建新对象。所以在使用该对象的API编程时,推荐使用get方法。在FileSystem中除了上述获取对象的静态方法外,还有一些其它方法,比如用于创建目录的mkdirs,移动文件的moveFromLocalFile和moveToLocalFile,复制文件的copyToLocalFile和copyFromLocalFile,创建文件的create等,这些方法都多个重载方法,可以根据需要选择合适的方法。需要注意的是create方法并未创建文件,而是返回了FSDataOutputStream对象,使用该对象就可以向文件中写入数据。
除了FileSystem外,还有几个类需要了解,分别为Path、FileStatus、FsStatus。其中FsStatus用于表示文件系统的容量,已用和未用空间,方法分别为getCapacity、getUse和getRemaining。FileStatus用于获取文件系统的元数据,比如文件大小,块大小,文件所有者,是否为目录或者文件等。Path表示文件系统的一个文件或者目录,也就是Path对象对应了文件系统的一个路径,该路径既可以是文件也可以是目录,如果以/开头,那么Path所表达的为绝对路径。Path提供了一些用于判断是否为根路径、是否绝对路径的方法,还有用于getName和toUri等方法。
最后综合上述知识,编写一小段简单的代码,该代码演示了部分方法的使用方式,更多内容可以参考官方API:
public class FileSystemTest { public static void main(String[] args) { Configuration conf = new Configuration(); try { FileSystem local = FileSystem.getLocal(conf); FileSystem hdfs = FileSystem.get(URI.create("hdfs://localhost:9000"),conf); FsStatus fsLocal = local.getStatus(); FsStatus fsHdfs = hdfs.getStatus(); System.out.println("Capacity: " + fsLocal.getCapacity()/1024/1024/1024 +"GB"); System.out.println("Remaining :" + fsLocal.getRemaining()/1024/1024/1024 +"GB"); System.out.println("Used: " + fsLocal.getUsed()/1024/1024/1024 +"GB"); System.out.println(); System.out.println("Capacity: " + fsHdfs.getCapacity()/1024/1024/1024 +"GB"); System.out.println("Remaining :" + fsHdfs.getRemaining()/1024/1024/1024 +"GB"); System.out.println("Used: " + fsHdfs.getUsed()/1024/1024/1024 +"GB"); Path hdfsPath = new Path("/user/hadoop/"); lsFile(hdfsPath,hdfs); } catch (IOException e) { e.printStackTrace(); } } public static void lsFile(Path path,FileSystem fsystem) throws IOException{ FileStatus fs = fsystem.getFileStatus(path); if(fs.isDirectory()){ System.out.println("Directory: " + fs.getPath().toString()); FileStatus[] fss = fsystem.listStatus(path); for(FileStatus f : fss){ lsFile(f.getPath(),fsystem); } }else{ System.out.println("File Name is: " + fs.getPath().toString() +",Size is: " + fs.getLen()/1024 + "KB"); } } }