HDFS—Java API操作

原文作者: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();
}

猜你喜欢

转载自blog.csdn.net/sanmi8276/article/details/113064616