HDFS 客户端操作
基于 Hadoop 3.3.4 完全分布式集群的 HDFS Java API 实践,覆盖全部常用文件系统操作。
- 11 个 HDFS 操作完整覆盖(mkdir/upload/download/rename/delete)
- IO 流底层操作:FSDataInputStream / FSDataOutputStream
- 块信息读取与 DataNode 位置追踪(BlockLocation)
- 递归目录遍历与文件属性读取(LocatedFileStatus)
- 参数优先级验证:代码配置 > hdfs-site.xml > 集群默认
- JUnit 4 测试驱动开发,快速验证 HDFS 连通性
HDFS 客户端操作 — Java API 实践
项目概述
基于 Hadoop 3.3.4 完全分布式集群(1主2从),使用 Java API 实现 HDFS 客户端的完整操作。11 个 JUnit 测试用例覆盖了 HDFS 文件系统的全部常用操作。
环境配置
Maven 项目(Java 8),依赖 Hadoop 3.3.4 客户端库:
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>3.3.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.3.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>3.3.4</version>
</dependency>
客户端配置 hdfs-site.xml 指定默认副本数为 1(开发环境),运行时可通过代码配置覆盖。
操作分类
1. 目录管理
- 创建目录
testMkdirs():FileSystem.mkdirs()创建多级目录 - 删除目录
testDelete():FileSystem.delete()递归删除
2. 文件 CRUD
- 上传
testUpload():copyFromLocalFile()从本地拷贝到 HDFS - 下载
testDownload():copyToLocalFile()从 HDFS 拷贝到本地 - 重命名
testRename():FileSystem.rename()文件重命名
3. IO 流底层操作
- 流式上传
testUploadByIO():FileInputStream→FSDataOutputStream - 流式下载
testDownloadByIO():FSDataInputStream→FileOutputStream
使用 IOUtils.copyBytes() 完成流拷贝,手动管理流关闭,理解 HDFS 数据流的底层机制。
4. 数据块级操作
- 按块读取
testReadFileSeek1()/testReadFileSeek2():通过FSDataInputStream.seek()定位到指定偏移,验证 HDFS 128MB 块的物理分片特性
5. 元数据查询
- 文件详情
testListFiles():递归遍历,输出文件名/大小/权限/所有者 - 根目录状态
testListStatus():判断文件还是目录 - 块位置追踪
testFileDetailWithBlocks():通过BlockLocation[]获取每个数据块的偏移、长度和所在的 DataNode 主机列表
设计决策
参数优先级验证
Hadoop 配置有明确的优先级链:代码中 conf.set() > 资源文件 hdfs-site.xml > 集群默认配置。testUploadWithPriority() 通过设置 dfs.replication=2 验证了这一优先级,可以在 Web UI 上确认副本数变化。
JUnit 驱动开发
每个操作封装为独立的 @Test 方法,可单独运行。这种设计使得:
- 集群连通性验证只需运行一个测试
- 每个操作可独立调试
- 测试即文档,新开发者通过看测试用例就能理解全部功能
关键代码模式
// 标准连接模式
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(
new URI("hdfs://192.168.88.100:9000"),
conf, "root"
);
// ... 操作 ...
fs.close();
所有测试用例使用相同的连接创建模式,通过 FileSystem.get() 获取文件系统实例。其中参数优先级验证通过提前 conf.set() 改写配置项,展示了 Hadoop 配置体系的灵活性。
xmlMaven 依赖配置
pom.xml:Hadoop 3.3.4 客户端依赖 + JUnit 4 测试框架。编译目标 Java 8。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>3.3.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.3.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>3.3.4</version>
</dependency>
</dependencies>java连接建立与目录管理(mkdir / delete)
通过 FileSystem.get() 连接 HDFS 集群,创建和删除目录。DFS 配置使用 NameNode URI 直连。
public class HdfsClient {
// 创建目录
@Test
public void testMkdirs() throws Exception {
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(
new URI("hdfs://192.168.88.100:9000"),
conf, "root"
);
fs.mkdirs(new Path("/test/hdfs/demo"));
fs.close();
}
// 删除目录(递归)
@Test
public void testDelete() throws Exception {
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(
new URI("hdfs://192.168.88.100:9000"),
conf, "root"
);
fs.delete(new Path("/test/hdfs/demo"), true);
fs.close();
}
}java文件上传(普通 & 参数优先级测试)
copyFromLocalFile 上传本地文件到 HDFS。参数优先级演示:代码中设置 dfs.replication=2 覆盖 hdfs-site.xml 的默认值 1。
// 普通上传
@Test
public void testUpload() throws Exception {
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(
new URI("hdfs://192.168.88.100:9000"), conf, "root"
);
fs.copyFromLocalFile(
new Path("D:/test.txt"),
new Path("/test/hdfs/demo/test.txt")
);
fs.close();
}
// 参数优先级测试:代码配置 > 资源文件 > 集群默认
@Test
public void testUploadWithPriority() throws Exception {
Configuration conf = new Configuration();
conf.set("dfs.replication", "2");
FileSystem fs = FileSystem.get(
new URI("hdfs://192.168.88.100:9000"), conf, "root"
);
fs.copyFromLocalFile(
new Path("D:/test.txt"),
new Path("/test/hdfs/demo/priority_test.txt")
);
fs.close();
}javaIO 流方式:文件上传与下载
底层流操作:用 FSDataOutputStream 写 HDFS、FSDataInputStream 读 HDFS,通过 IOUtils.copyBytes 完成流拷贝。
// IO 流方式上传
@Test
public void testUploadByIO() throws Exception {
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(
new URI("hdfs://192.168.88.100:9000"), conf, "root"
);
FileInputStream in = new FileInputStream("D:/test.txt");
FSDataOutputStream out = fs.create(
new Path("/test/hdfs/demo/io_upload.txt")
);
IOUtils.copyBytes(in, out, conf);
IOUtils.closeStream(in);
IOUtils.closeStream(out);
fs.close();
}
// IO 流方式下载
@Test
public void testDownloadByIO() throws Exception {
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(
new URI("hdfs://192.168.88.100:9000"), conf, "root"
);
FSDataInputStream in = fs.open(
new Path("/test/hdfs/demo/io_upload.txt")
);
FileOutputStream out = new FileOutputStream("D:/io_download.txt");
IOUtils.copyBytes(in, out, conf);
IOUtils.closeStream(in);
IOUtils.closeStream(out);
fs.close();
}java数据块读取:按块偏移定位(seek)
演示 HDFS 块的概念。FSDataInputStream 支持 seek() 跳到指定偏移,每块 128MB。testReadFileSeek1 读前 128MB,testReadFileSeek2 从 128MB 位置开始读。
// 读取第一块(前 128MB)
@Test
public void testReadFileSeek1() throws Exception {
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(
new URI("hdfs://192.168.88.100:9000"), conf, "root"
);
FSDataInputStream fis = fs.open(
new Path("/test/hdfs/demo/test.txt")
);
FileOutputStream fos = new FileOutputStream("D:/test.txt.part1");
byte[] buf = new byte[1024];
for (int i = 0; i < 1024 * 128; i++) {
fis.read(buf);
fos.write(buf);
}
IOUtils.closeStream(fos);
IOUtils.closeStream(fis);
fs.close();
}
// 读取第二块(从 128MB 位置 seek)
@Test
public void testReadFileSeek2() throws Exception {
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(
new URI("hdfs://192.168.88.100:9000"), conf, "root"
);
FSDataInputStream fis = fs.open(
new Path("/test/hdfs/demo/test.txt")
);
FileOutputStream fos = new FileOutputStream("D:/test.txt.part2");
fis.seek(1024 * 1024 * 128);
IOUtils.copyBytes(fis, fos, conf);
IOUtils.closeStream(fos);
IOUtils.closeStream(fis);
fs.close();
}java文件详情与块位置追踪(元数据读取)
递归遍历目录下所有文件,输出文件名、大小、权限、所有者,以及每个数据块的偏移量、长度和数据节点位置。
// 递归查看文件详情(含块所在主机)
@Test
public void testFileDetailWithBlocks() throws Exception {
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(
new URI("hdfs://192.168.88.100:9000"), conf, "root"
);
RemoteIterator<LocatedFileStatus> listFiles =
fs.listFiles(new Path("/test/hdfs/demo"), true);
while (listFiles.hasNext()) {
LocatedFileStatus status = listFiles.next();
System.out.println("路径:" + status.getPath());
System.out.println("大小:" + status.getLen() + " 字节");
System.out.println("权限:" + status.getPermission());
System.out.println("所有者:" + status.getOwner());
BlockLocation[] blocks = status.getBlockLocations();
for (BlockLocation blk : blocks) {
System.out.println(" 块偏移:" + blk.getOffset() +
",长度:" + blk.getLength());
System.out.println(" DataNode:" +
String.join(",", blk.getHosts()));
}
}
fs.close();
}| 路径 | 说明 | 行数 |
|---|---|---|
pom.xml | Maven 项目配置(Hadoop 3.3.4 + JUnit 4) | 45 |
src/main/resources/hdfs-site.xml | HDFS 客户端配置(副本数 = 1) | 8 |
src/main/java/com/atguigu/hdfs/HdfsClient.java | HDFS 客户端 11 个操作完整实现 | 262 |