环境准备

虽然我们编写 java 代码的电脑是作为客户端去连接 hdfs 服务器,但是 hdfs 要求如果要读写hdfs就需要在客户端也安装 hadoop。但是hdfs官方又没有Windows版的安装包:

  • 如果是准备在 Linux / mac 环境编写 java 代码连接HDFS,则只需要在Linux系统上也安装一下 hadoop 即可(将Hadoop压缩包解压,然后配置环境变量)。
  • 如果准备在 Windows 环境下编写 java 代码连接 hdfs,则需要在windows系统中安装与hadoop服务器对应版本的winutils.exe和hdfs.dll(因为hdfs默认不支持windows安装)。winutils.exe工具可以在 github 上找到,也可以自己编译hadoop源码得到。

否则会报错:

  1. HADOOP_HOME and hadoop.home.dir are unset

编写Demo

  1. 创建Maven工程,添加依赖:

    1. <dependency>
    2. <groupId>org.apache.hadoop</groupId>
    3. <artifactId>hadoop-client</artifactId>
    4. <!-- 注意版本和Hadoop服务器版本一致,否则可能会不兼容 -->
    5. <version>3.2.3</version>
    6. </dependency>
    7. <dependency>
    8. <groupId>junit</groupId>
    9. <artifactId>junit</artifactId>
    10. <version>4.12</version>
    11. <scope>test</scope>
    12. </dependency>
    13. <dependency>
    14. <groupId>org.slf4j</groupId>
    15. <artifactId>slf4j-log4j12</artifactId>
    16. <version>1.7.30</version>
    17. </dependency>
  2. 配置log4j日志:log4j.properties

    1. log4j.rootLogger=INFO, stdout
    2. log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    3. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    4. log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
    5. log4j.appender.logfile=org.apache.log4j.FileAppender
    6. log4j.appender.logfile.File=target/hadoop-client.log
    7. log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
    8. log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
  3. 编写测试Demo代码:

    1. 获取客户端对象
    2. 执行相关的操作命令
    3. 关闭资源 ```java package com.study.hdfs;

import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.junit.After; import org.junit.Before; import org.junit.Test;

import java.io.IOException; import java.net.URI; import java.net.URISyntaxException;

public class HdfsClient {

  1. private FileSystem fs;
  2. @Before
  3. /**
  4. * 获取客户端对象
  5. */
  6. public void init() throws URISyntaxException, IOException, InterruptedException {
  7. Configuration configuration = new Configuration();
  8. // 连接的集群NN地址
  9. String uri = "hdfs://hadoop102:8020";
  10. // 用户
  11. String user = "tengyer";
  12. fs = FileSystem.get(new URI(uri), configuration, user);
  13. }
  14. @After
  15. /**
  16. * 关闭资源
  17. */
  18. public void close() throws IOException {
  19. fs.close();
  20. }
  21. @Test
  22. public void testMkdirs() throws IOException, URISyntaxException, InterruptedException {
  23. // 在HDFS上创建一个文件夹
  24. fs.mkdirs(new Path("/xiyouji/huaguoshan"));
  25. }

}

  1. <a name="g50yB"></a>
  2. # 常用 Java API
  3. <a name="P238i"></a>
  4. ## 创建文件夹
  5. ```java
  6. @Test
  7. public void testMkdirs() throws IOException, URISyntaxException, InterruptedException {
  8. // 在HDFS上创建一个文件夹
  9. fs.mkdirs(new Path("/xiyouji/huaguoshan"));
  10. }

上传文件

  1. @Test
  2. /**
  3. * 测试上传
  4. */
  5. public void testPut() throws IOException {
  6. // 上传完毕是否删除原数据
  7. boolean delSrc = false;
  8. // 如果hdfs上已有该文件,是否允许覆盖,(不允许覆盖时,如果目的地已经存在该文件,则抛出异常)
  9. boolean overwrite = false;
  10. // 源数据路径
  11. Path src = new Path("/app/testData/sunwukong.txt");
  12. // 目的地路径,可以加上协议写成完整路径 hdfs://hadoop102:8020/xiyouji/huaguoshan/,也可以不加
  13. Path dst = new Path("/xiyouji/huaguoshan/");
  14. fs.copyFromLocalFile(delSrc, overwrite, src, dst);
  15. }

下载文件或文件夹

  1. @Test
  2. /**
  3. * 测试从hdfs下载
  4. */
  5. public void testGet() throws IOException {
  6. // 下载完毕后,是否删除hdfs上的源文件
  7. boolean delSrc = false;
  8. // hdfs源数据路径(文件或文件夹),也可以加上hdfs://hadoop102/写成完整路径
  9. Path src = new Path("/xiyouji/huaguoshan/sunwukong.txt");
  10. // 目的地路径是一个文件夹,程序会将hdfs中的文件下载到该文件夹。如果配置的有CRC校验,则文件还会同时生成一个crc校验文件
  11. Path dst = new Path("/app/testData/"); // 目的文件夹路径
  12. boolean useRawLocalFileSystem = true; // 是否进行CRC完整性校验。true则不生成crc校验文件,false会生成
  13. fs.copyToLocalFile(delSrc, src, dst, useRawLocalFileSystem);
  14. }

删除hdfs的文件或文件夹

  1. @Test
  2. /**
  3. * 测试删除hdfs文件
  4. */
  5. public void testRM() throws IOException {
  6. // 要删除的路径(文件或文件夹)
  7. Path path = new Path("/xiyouji/huaguoshan/sunwukong.txt");
  8. // 是否递归删除,删除非空文件夹时需要递归删除文件夹下的内容,类似 rm 的 -r 参数。删除文件或空文件夹时可以不递归
  9. boolean recursive = false;
  10. fs.delete(path, recursive);
  11. }

文件(或文件夹)移动和重命名

  1. @Test
  2. /**
  3. * 测试移动和重命名hdfs文件
  4. */
  5. public void testMV() throws IOException {
  6. // Path src = new Path("/xiyouji/huaguoshan/sunwukong.txt");
  7. // Path dst = new Path("/xiyouji/huaguoshan/meihouwang.txt");
  8. // fs.rename(src, dst); // 重命名
  9. // 文件移动位置并重命名
  10. fs.rename(new Path("/xiyouji/huaguoshan/meihouwang.txt"), new Path("/xiyouji/sunxingzhe.txt"));
  11. }

获取文件夹下的文件信息(listFiles方式)

  1. @Test
  2. /**
  3. * 列举文件夹下文件详情, ls 命令
  4. */
  5. public void testLS() throws IOException {
  6. Path path = new Path("/xiyouji");
  7. boolean recursive = false; // 是否递归
  8. // 类似ls命令,返回值是一个迭代器
  9. RemoteIterator<LocatedFileStatus> listFilesIterator = fs.listFiles(path, recursive);
  10. while(listFilesIterator.hasNext()) {
  11. LocatedFileStatus fileStatus = listFilesIterator.next(); // 获取到文件属性
  12. Path filePath = fileStatus.getPath();
  13. String owner = fileStatus.getOwner();
  14. String group = fileStatus.getGroup();
  15. long blockSize = fileStatus.getBlockSize();
  16. short replication = fileStatus.getReplication();
  17. System.out.println(fileStatus);
  18. }
  19. }

获取文件夹下的文件信息(listStatus方式)

  1. @Test
  2. /**
  3. * 通过listStatus获取指定路径下的所有文件属性
  4. */
  5. public void testFileStatus() throws IOException {
  6. Path path = new Path("/jinguo");
  7. FileStatus[] fileStatuses = fs.listStatus(path);
  8. for (FileStatus fileStatus : fileStatuses) {
  9. System.out.println("------------------------");
  10. // 获取文件名称
  11. System.out.println(fileStatus.getPath().getName());
  12. // 判断是文件还是文件夹
  13. if (fileStatus.isFile()) {
  14. System.out.println("是一个文件");
  15. }
  16. }
  17. }

修改配置项

使用配置文件配置

java程序中,也可以在resources中添加hdfs-site.xml等配置文件来修改某些配置项的值。
例如在resources下创建hdfs-site.xml:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
  3. <configuration>
  4. <!-- 配置副本数量,程序中的hdfs-site.xml会覆盖hadoop软件中的该项值 -->
  5. <property>
  6. <name>dfs.replication</name>
  7. <value>4</value>
  8. </property>
  9. </configuration>

此时执行上传文件操作时,上传到hdfs上的文件副本数量就变为了4个。

使用configuration对象配置

java程序中,还可以通过configuration对象修改配置项的值,例如:

  1. Configuration configuration = new Configuration();
  2. // 将文件副本数量配置为5
  3. configuration.set("dfs.replication", "5");
  4. String uri = "hdfs://hadoop102:8020"; // 连接的集群NN地址
  5. String user = "tengyer"; // 用户
  6. fs = FileSystem.get(new URI(uri), configuration, user);

配置项优先级

参数的优先级,以hdfs-xxx.xml配置文件为例(越往下优先级越高,下面的配置会覆盖上面的):

  • hadoop软件中的hdfs-default.xml
  • hadoop软件中的hdfs-site.xml
  • 在项目的resources下的hdfs-site.xml配置文件
  • 程序中的Configuration对象中配置的值