Hadoop是用java语言实现的,因此HDFS有很好的java接口用以编程,重点就是Hadoop的FileSystem类,它是所有文件系统的抽象类,HDFS实例(DistributedFileSystem)也是基于它实现的。本部分主要介绍如何通过使用HDFS的java接口来编写程序。


1、如何运行一个Hadoop程序

  当我们要写一个Hadoop的应用程序时,因为要用到hadoop的第三方依赖包,所以最好的方法是使用maven项目管理工具来构建,可以方便的管理所有第三方jar包。

  完成程序编写后,要在hadoop集群或者我们的伪分布式模式下运行这个作业,那就需要将代码打包成一个jar文件,Hadoop会在集群上发布这个文件,如果我们使用maven构建,那么maven在mvn install之后就正好生成了一个可用的jar文件,默认在target目录下。

  假设我们的类名为:URLCat,那么运行以下命令就可以执行程序。

$ export HADOOP_CLASSPATH=target/URLCat.jar
$ hadoop URLCat [参数]

  当然,如果我们不使用maven,那么首先需要下载对应的jar包放到工程目录下,然后在程序编写完成后,先完成编译,然后再手动生成jar包,从而运行程序。

$ javac URLCat.java
$ jar cvf URLCat.jar URLCat.class
$ export HADOOP_CLASSPATH=URLCat.jar
$ hadoop URLCat [参数]

2、使用java从HDFS读取数据

  要从Hadoop中读取文件,有两种方法:一是使用java.net.URL对象打开数据流,从中读取数据;二是使用FileSystem API来打开一个文件的输入流。方法二更常用一些。

(1)从URL读取数据

  这种方法比较简单,但是为了让java程序可以识别hadoop的hdfs URL还需要一些额外的工作:通过FsUrlStreamHandlerFactory实例来调用java.net.URL对象的setURLStreamHandlerFactory方法,另外需要注意的是这个方法每个虚拟机只能调用一次,所以一般使用静态方法。

  在输入流和输出流之间复制数据可以使用简单的IOUtils类。

import java.io.InputStream;
import java.net.URL;
import org.apache.hadoop.fs.FsUrlStreamHandlerFactory;
import org.apache.hadoop.io.IOUtils; public class URLCat {
static { //每个虚拟机只能调用一次
URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
} public static void main(String[] args) throws Exception {
InputStream in = null;
try {
in = new URL(args[0]).openStream();
IOUtils.copyBytes(in, System.out, 4096, false); //后两个参数:缓冲区大小、是否关闭数据流
} finally {
IOUtils.closeStream(in);
}
}
}

(2)通过FileSystem API读取数据

  更常用的是,通过FileSystem API来打开一个文件的输入流,Hadoop文件系统中通过Path对象来代表文件,可以将路径视为一个Hadoop文件系统的URI,如:hdfs://localhost/user/gz.shan/2.txt.

  FileSystem是一个通用的文件系统API,想要从中读取文件,首先我们需要获取文件系统的实例。比如:想获取本地文件系统的实例,可以通过FileSystem.getLocal(Configuration conf)方法。

  这里我们是获取HDFS的实例,可以通过FileSystem.get(URI uri,Configuration conf)方法。有了FileSystem实例后,通过调用open方法来获取文件输入流。

import java.io.InputStream;
import java.net.URI; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils; public class FileSystemCat { public static void main(String[] args) throws Exception {
String uri = args[0];
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(uri), conf); //获取文件系统实例
InputStream in = null;
try {
in = fs.open(new Path(uri)); //获取文件输入流
IOUtils.copyBytes(in, System.out, 4096, false);
} finally {
IOUtils.closeStream(in);
}
}
}

  实际上,这个open方法返回的并不是标准的java.io类对象,而是FSDataInputStream对象(一个继承了java.io.DataInputStream的特殊类)。

  FSDataInputStream最大的好处是支持随机访问,可以从流的任意位置读取数据。

public class FsDataInputStream extends DataInputStream implements Seekable,PositionedReadable{}

public interface Seekable {
void seek(long pos);
long getPos();
} public interface PositionedReadable{
public int read(long position,byte[] buffer,int offset.int length);
public void readFully(long position,byte[] buffer,int offset.int length);
public void readFully(long position,byte[] buffer);
}

  Seekable接口支持长文件中找到指定位置,并有一个查询当前位置相对于文件起始偏移量的方法。seek()可以移动到文件中任意一个绝对位置,但是seek是一个相对开销比较高的操作,要谨慎使用,避免大量使用。

  PositionedReadable接口允许从一个指定偏移量处读取文件的一部分,read方法从指定position处读取至多length字节的数据存入到缓冲区buffer的指定偏移量offset处,返回实际读到的字节数。readfull将指定length长度的字节数据读到buffer中,除非已经到文件末尾,则抛出EOFException。

3、使用java向HDFS写入数据

  fileSystem有一系列新建文件的方法,最简单的是给准备建的文件指定一个Path对象,然后返回一个用于写入数据的输出流。新建文件使用的是create(Path f)方法,还有一种是在一个已有文件的末尾追加:append(Path f)。

  需要注意的是:create方法还有一个重载方法Progressable用于传递回调接口,从而将数据写入进度通知给应用,这在mapreduce中有广泛的应用。

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable; public class FileCopyWithProgress {
public static void main(String[] args) throws Exception {
String localSrc = args[0];
String dst = args[1]; InputStream in = new BufferedInputStream(new FileInputStream(localSrc)); Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(dst), conf);
OutputStream out = fs.create(new Path(dst), new Progressable() {
public void progress() {
System.out.print(".");
}
}); IOUtils.copyBytes(in, out, 4096, true);
}
}

  同样的,这里的create方法返回的并不是标准的OutputStream,而是FSDataOutputStream对象。不同的是:它只有一个查询文件当前位置的方法,不允许在文件中定位,也就是只允许顺序写入或者末尾追加,不支持其他位置的随意写入。

public class FSDataOutputStream extends DataOutputStream implements syncable{
public long getPos();
}

4、文件系统的查询

  除了文件读写以外,任何一个文件系统的重要特征就是提供其目录结构浏览和检索他所在文件和目录相关信息的功能。

(1)文件元数据:FileStatus

  FileStatus类封装了文件系统中文件和目录的元数据,包括文件长度、块大小、副本、修改时间、所有者、权限信息等。使用FileSystem的getFileStatus方法可以 获取文件或者目录的FileStatus对象。

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"));
}

(2)列出文件:listStatus

  另外一个有用的功能是列出目录中的,这就是FileSystem的listStatus方法的功能。当参数是一个文件时,会返回数组形式的长度为1的FileStatus对象,当参数是一个目录时,会返回一个数组,包含0个或者多个FileStatus对象,表示此目录中的文件和目录。

  还有一个参数可以用于指定PathFilter来限制匹配的文件和目录。注意:也可以指定一组路径,如果指定一组路径,其执行结果相当于依次轮流传递每条路径并对其调用listStatus方法,返回的结果存入一个数组。

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); //将FileStatus对象数组转为Path数组
for (Path p : listedPaths) {
System.out.println(p);
}
}
}

(3)文件模式

  文件模式其实并不难理解,有时我们需要处理一批文件,需要是使用通配符来匹配多个文件,这叫“通配”,也就是我们熟知的正则表达式。文件模式就是用来做通配的。

  HDFS中的通配方法是:globStatus(),使用方法就类似于我们使用的正则表达式,不再赘述。

(4)PathFilter对象

  通配符可以完成匹配一批文件,但是有些情况它无法做到:比如排除一个特定的文件。这就需要过滤器,FileSystem的listStatus和globStatus都提供了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);
}
}

5、其他操作

(1)目录

  FileSystem提供了创建一个目录的方法:public boolean mkdirs(Path f),创建成功返回true,但是通常情况下我们不需要显示创建目录,因为调用create方法时会自动创建父目录。

(2)删除数据

  FileSystem的delete方法可以用于删除文件或者目录:public boolean delete(Path f,boolean recursive),只有当recursive的值为true时,非空目录及其内容才会被删除。

总结

  这几篇文章相当于《Hadoop权威指南》的读书笔记。至此,从理论和实践的角度对HDFS有了一个比较清晰的认识,后续再陆续介绍Hadoop生态系统中的其他组件。

【Hadoop】四、HDFS的java接口的更多相关文章

  1. 【Hadoop】HDFS的java客户端编写

    项目使用了Maven  Project 快速进行HDFS 客户端程序测试 客户端操作系统:win10 64位 JDK: 1.7.0_79 开发工具 :Eclipse Luna pom.xml < ...

  2. CentOS Hadoop格式化HDFS异常java.net.UnknownHostException

    #bin/hadoop namenode -format DEPRECATED: Use of this script to execute hdfs command is deprecated. I ...

  3. 一脸懵逼学习hadoop之HDFS的java客户端编写

    1:eclipse创建一个项目,然后导入对应的jar包: 鼠标右击项目,点击properties或者alt+enter快捷键--->java build path--->libraries ...

  4. HDFS的java接口——简化HDFS文件系统操作

    今天闲来无事,于是把HDFS的基本操作用java写出简化程序出来给大家一些小小帮助! package com.quanttech; import org.apache.hadoop.conf.Conf ...

  5. hdfs的java接口简单示例

    public class HDFSDemo { private FileSystem fs = null; @Before public void init() throws IOException, ...

  6. hadoop执行hdfs文件到hbase表插入操作(xjl456852原创)

    本例中需要将hdfs上的文本文件,解析后插入到hbase的表中. 本例用到的hadoop版本2.7.2 hbase版本1.2.2 hbase的表如下: create 'ns2:user', 'info ...

  7. hadoop2.5.2学习及实践笔记(六)—— Hadoop文件系统及其java接口

    文件系统概述 org.apache.hadoop.fs.FileSystem是hadoop的抽象文件系统,为不同的数据访问提供了统一的接口,并提供了大量具体文件系统的实现,满足hadoop上各种数据访 ...

  8. 大数据时代之hadoop(四):hadoop 分布式文件系统(HDFS)

    分布式文件系统即是网络中多台计算机组合在一起提供一个统一存储及管理的系统. Hadoop提供了一个文件系统接口和多个分布式文件系统实现,其中比较重要的就是HDFS(Hadoop Distributed ...

  9. HDFS的接口(命令行接口和Java接口)--笔记

    HDFS 文件的系统访问的接口 1.Hadoop的shell命令脚本 hadoop fs -ls   列出某一个目录下的文件 hadoop fs -lsr 递归的方式列出所有文件 hadoop fs ...

随机推荐

  1. Codeforces Round #323 (Div. 2) D. Once Again... 暴力+最长非递减子序列

                                                                                  D. Once Again... You a ...

  2. 【codevs1306】广播操的游戏

    求字符串内的非空子串的数量 后缀数组!!! #include<algorithm> #include<cstdlib> #include<cstring> #inc ...

  3. 在64位的ubuntu 14.04 上开展32位Qt 程序开发环境配置(pro文件中增加 QMAKE_CXXFLAGS += -m32 命令)

    为了能中一个系统上开发64或32位C++程序,费了些周折,现在终于能够开始干过了.在此记录此时针对Q5.4版本的32位开发环境配置过程. 1. 下载Qt 5.4 的32位版本,进行安装,安装过程中会发 ...

  4. C# winfrom TCP 服务端和客户端(链接)

    1.C#Winform TCP 之服务端: 可以参考下面链接,比较好.第二个链接可以看看,提供了一个思路. http://www.cnblogs.com/guolebin7/archive/2013/ ...

  5. mysql —— 利用Navicat 导出和导入数据库

    Navicat for MySql 导出数据库方法: 打开Navicat for MySql,在要导出的数据库上面右击鼠标,点击“转储SQL 文件”→“数据和结构”. 找到合适的路径,点击“保存”. ...

  6. 【转】整套完整安全的API接口解决方案

    原文地址:http://www.cnblogs.com/hubro/p/6248353.html 在各种手机APP泛滥的现在,背后都有同样泛滥的API接口在支撑,其中鱼龙混杂,直接裸奔的WEB API ...

  7. JavaScript 解析读取XML文档 实例代码(转)

    JavaScript解析读取XML文件,主要就是加载并解析XML文件,然后就可以测试解析的XML文件的内容,打印输出来. 在线演示:http://demo.jb51.net/js/2012/readx ...

  8. CodeForces 731A Night at the Museum (水题)

    题意:给定一个含26个英语字母的转盘,问你要得到目标字符串,至少要转多少次. 析:分别从顺时针和逆时针进行,取最小的即可. #pragma comment(linker, "/STACK:1 ...

  9. DFS(连通块) HDU 1241 Oil Deposits

    题目传送门 /* DFS:油田问题,一道经典的DFS求连通块.当初的难题,现在看上去不过如此啊 */ /************************************************ ...

  10. 思维题 UVA 10881 Piotr's Ants

    题目传送门 /* 题意:在坐标轴上一群蚂蚁向左或向右爬,问经过ts后,蚂蚁的位置和状态 思维题:本题的关键1:蚂蚁相撞看作是对穿过去,那么只要判断谁是谁就可以了 关键2:蚂蚁的相对位置不变 关键3:o ...