原文作者:jiangw-Tony
原文地址:HDFS基础使用
hdfs 在生产应用中主要是客户端的开发,其核心步骤是从 hdfs 提供的 api 中构造一个 HDFS的访问客户端对象,然后通过该客户端对象操作(增删改查)HDFS 上的文件。
一、环境搭建
1、创建一个Maven工程HdfsClientDemo
2、在该项目的pom.xml文件中添加以下代码:导入相应的依赖坐标+日志添加
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.8.2</version>
</dependency>
</dependencies>
3、在java下创建了cn.itcast.hdfs包,在目录下新建了Java文件
到此表示,我们利用 HDFS 的 api 编写业务代码所依赖的 jar 包都添加完成,接下来便可以愉快的玩耍代码了。
二、FileSystem实例获取讲解(重点)
在 java 中操作 hdfs,首先要获得一个客户端实例:
- Configuration conf = new Configuration()
- FileSystem fs = FileSystem.get(conf)
而我们的操作目标是 HDFS,所以获取到的 fs 对象应该是 DistributedFileSystem 的实例;get 方法是从何处判断具体实例化哪种客户端类呢?从 conf 中的一个参数 fs.defaultFS 的配置值判断;如果我们的代码中没有指定 fs.defaultFS,并且工程 classpath 下也没有给定相应的配置,conf中的默认值就来自于 hadoop 的 jar 包中的 core-default.xml,默认值为: file:///,则获取的将不是一个 DistributedFileSystem 的实例,而是一个本地文件系统的客户端对象。
DistributedFileSystem实例所具备的方法如下:
三、HDFS常用JAVA API代码演示
1. 建立文件夹
2. 上传文件
3. 下载文件
4. 删除文件或文件夹
5.重命名文件或者文件夹
6.查看目录信息,只显示该文件夹下的文件信息
7.查看文件及文件夹信息
四、HDFS流式数据访问
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.junit.Before;
import org.junit.Test;
/**
* 相对那些封装好的方法而言的更底层一些的操作方式 上层那些 mapreduce spark 等运算
框架,去 hdfs 中获取数据的时候,就是调的这种底层的 api
*/
public class StreamAccess {
FileSystem fs = null;
@Before
public void init() throws Exception {
Configuration conf = new Configuration();
System.setProperty("HADOOP_USER_NAME", "root");
conf.set("fs.defaultFS", "hdfs:// hadoop01:9000");
fs = FileSystem.get(conf);
// fs = FileSystem.get(new URI("hdfs://hadoop01:9000"), conf, "hadoop");
}
@Test
public void testDownLoadFileToLocal() throws IllegalArgumentException,IOException {
// 先获取一个文件的输入流----针对 hdfs 上的
FSDataInputStream in = fs.open(new Path("/jdk-7u65-linux-i586.tar.gz"));
// 再构造一个文件的输出流----针对本地的
FileOutputStream out = new FileOutputStream(new File("c:/jdk.tar.gz"));
// 再将输入流中数据传输到输出流
IOUtils.copyBytes(in, out, 4096);
}
@Test
public void testUploadByStream() throws Exception {
// hdfs 文件的输出流
FSDataOutputStream fsout = fs.create(new Path("/aaa.txt"));
// 本地文件的输入流
FileInputStream fsin = new FileInputStream("c:/111.txt");
IOUtils.copyBytes(fsin, fsout, 4096);
}
/**
* hdfs 支持随机定位进行文件读取,而且可以方便地读取指定长度 用于上层分布式运
算框架并发处理数据
*/
@Test
public void testRandomAccess() throws IllegalArgumentException, IOException {
// 先获取一个文件的输入流----针对 hdfs 上的
FSDataInputStream in = fs.open(new Path("/iloveyou.txt"));
// 可以将流的起始偏移量进行自定义
in.seek(22);
// 再构造一个文件的输出流----针对本地的
FileOutputStream out = new FileOutputStream(new File("d:/iloveyou.line.2.txt"));
IOUtils.copyBytes(in, out, 19L, true);
}
}
五、经典案例
在 mapreduce 、spark 等运算框架中,有一个核心思想就是将运算移往数据,或者说,就是要在并发计算中尽可能让运算本地化,这就需要获取数据所在位置的信息并进行相应范围读取。以下模拟实现:获取一个文件的所有 block 位置信息,然后读取指定 block 中的内容。
@Test
public void testCat() throws IllegalArgumentException, IOException {
FSDataInputStream in = fs.open(new Path("/weblog/input/access.log.10"));
// 拿到文件信息
FileStatus[] listStatus = fs.listStatus(new Path("/weblog/input/access.log.10"));
// 获取这个文件的所有 block 的信息
BlockLocation[] fileBlockLocations = fs.getFileBlockLocations(
listStatus[0], 0L, listStatus[0].getLen());
// 第一个 block 的长度
long length = fileBlockLocations[0].getLength();
// 第一个 block 的起始偏移量
long offset = fileBlockLocations[0].getOffset();
System.out.println(length);
System.out.println(offset);
// 获取第一个 block 写入输出流
// IOUtils.copyBytes(in, System.out, (int)length);
byte[] b = new byte[4096];
FileOutputStream os = new FileOutputStream(new File("d:/block0"));
while (in.read(offset, b, 0, 4096) != -1) {
os.write(b);
offset += 4096;
if (offset > length)
return;
}
os.flush();
os.close();
in.close();
}