1.FileSystem的获取。
我们可以在Hadoop中使用FileSystem API来打开一个文件的输入流,然后我们可以对文件进行各种的操作实现。
FileSystem是一个通用的文件系统API,FileSystem的获取方法有以下几种(以下所有相关代码接口实现均为手打,不保证完全正确,如有出错敬请谅解。):
public statis FileSystem get(Configuration conf) throws IOException
public statis FileSystem get(URI uri , Configuration conf) throws IOException
public statis FileSystem get(URI uri , Configuration conf ,String user) throws IOException
共有三种参数不同的静态工厂方法,所以想要得到一个FileSystem对象的话,是new不出来的。
需要注意的是这里的Configuration 对象封装了客户端或服务器的配置,通过设置配置文件读取类路径来实现,这里的Configuration 不要导错包了。
import org.apache.hadoop.conf.Configuration;
第一个方法返回的是默认文件系统(在core-site.xml中指定的,如果没有指定,则使用默认的本地文件系统)。
这里需要解释一点就是,在core-site.xml中的fs.default.name(2.x版本中使用的是fs.fs.defaultFS)属性就是指定了HDFS的namenode和默认文件系统,也是因为这个属性的原因,所以当用户在运行HDFS的时候,HDFS必须是服务器配置的默认文件系统。单为了操作方便,也允许在客户端配置中将其他文件系统指定为默认文件系统。
第二个方法通过给定的URI方法和权限来确定要使用的文件系统,如果给定的URI中没有指定方案,则返回默认的文件系统。
第三个方法添加了user参数,顾名思义就是让给定用户来访问文件系统,增加一定的安全性。
除了这三种之外还有一种方法可以获取本地文件系统。
public static LocalFileSystem getLocal(Configuration conf) throws IOException
2.FileSystem读取数据。
一旦获取到FileSystem实例之后,就可以对文件进行各种操作。
1.可以调用open()函数来获取文件的输入流:
public FSDataInputStream open(Path f) throws IOException public abstract FSDataInputStream open(Path f, int bufferSize) throws IOException
需要注意的是这里Path是在 org.apache.hadoop.fs.Path 包下的。
既然能够获取到文件的输入流,那么我们很容易就可以将文件的内容显示出来,在 org.apache.hadoop.io报下提供了一个IOUtils类,我们可以直接使用该类的copyBytes方法将输入流中的内容输出到System.out中,这样就能够展现出读取文件的作用,就能够直接将HDFS中的文件通过流的形式进行提取和存储。
调用方法如下:第一个参数就是输入流,第二个是输出流,第三个是字节数,第四个是是否关闭数据流(可以在这里设置为false,但是那么就需要在后面手动关闭)。
in = fs.open(new Path(uri));//根据HDFS的uri获取一个数据流 IOUtils.copyBytes(in, System.out, 4096 ,false);//进行数据流之间的拷贝 IOUtils.closeStream(in);//关闭数据流
2.可以看到通过open对象会返回一个FSDataInputStream对象,这个对象并不是标准的java.io类对象。这个类是继承了java.io.DataInputStream接口的一个特殊类,并支持随机访问,可以从流的任意位置读取数据。
package org.apache.hadoop.fs; public class FSdataInputStream extends DataInputStream implements Seekable,PositionedReadable{ }
Seekable接口支持在文件中找到指定位置,并提供一个查询当前位置相对于文件起始位置偏移量getPos()的方法。
public interface Seekable { void seek(long pos) throws IOException long getPos() throws IOException boolean seekToNewSource(long targetPos) throws IOException//这个方法在书中有提到,但是本人在API接口中没有找到 }
调用getPos()方法会返回对于文件开头的当前偏移量。
调用seek()方法的时候,如果定位大于文件长度的位置就会引发IOException异常。
seek()和java.io.InputStream中的skip()有些不同,seek()可以移到文件中任意一个绝对位置,skip()则只能相对于当前位置定位到另一个新位置。
所以我们可以使用seek()方法来将一个数据流读取两次,先使用上述中的copyBytes方法读取一次流数据,然后调用seek方法定位到流的开始位置,再次读取。
in = fs.open(new Path(uri)); IOUtils.copyBytes(in, System.out, 4096 ,false);//进行数据流之间的拷贝 in.seek(0);//回到数据流开始的位置 IOUtils.copyBytes(in, System.out, 4096 ,false);//再次读取
除了Seekable接口之外,还实现了PositionedReadable接口,从一个指定偏移量处读取文件的一部分。
public