HDFS的Java API操作

通过Java代码操作HDFS集群

目录

引言

Idea连接HDFS

第一步:引入HDFS依赖

第一种引入方式(jar包)

第二种引入方式(使用maven引用)

引入项目依赖的时候:

maven项目的几个核心的生命周期:

第二步:idea操作

配置HDFS的访问路径

单元测试

HDFS的JavaAPI基本操作

Maven依赖准备

hdfs文件系统的API使用

操作HDFS:

连接HDFS文件系统------是必备操作(见二、idea连接HDFS)

对HDFS进行操作

递归获取HDFS所有文件

FileSystem其他方法

IO流操作HDFS

利用IO流实现文件的上传和下载

利用IO流实现定位文件读取


引言

要想在Windows上操作HDFS,首先需要在Windows上安装HDFS。由于Hadoop官网没有提供Windows下载版本,所以需要对 Hadoop.tar.gz进行两次解压(推荐用7-zip软件),解压完成后添加相应环境变量:HADOOP_HOME、Path

 

Idea连接HDFS

第一步:引入HDFS依赖

第一种引入方式(jar包)

自己找jar包然后插入程序中(HDFS编程所需jar包都在Hadoop安装目录的share目录下,此处将jar包归类了三个文件夹

         导入到idea中:

 

第二种引入方式(使用maven引用)

maven项目创建后是如下结构:

          

                其中:

        src

               main

                javaJava源代码

                resourceJava中的一些静态紫竹院,如文件、图片、HTML文件等

               test

                Java:专门用来编写Java Junit单元测试代码

引入项目依赖的时候:

gav....
  scope:引入的依赖jar包的一个作用范围
  runtime:项目运行过程中也要使用
  test:项目在测试过程中才能去使用
  provided:项目在编译时和运行时都起作用

maven项目的几个核心的生命周期:

 clean:清楚上一次编译的结果

compile:编译源代码
test:执行maven项目的test包下的单元测试代码
package:如果test阶段测试通过,那么将项目打包成对应的包
install:创建maven项目的时候也指定了当前项目的gav坐标,执行install之后会将项目放入本地的maven仓库

 

第二步:idea操作

引入配置文件,指定我们在去连接HDFS的时候我们应该采取什么样的配置:

import org.apache.hadoop.conf.Configuration;
 
 
Configuration conf = new Configuration();

 

  • 配置HDFS的访问路径

conf.set("fs.defaultFS", "hdfs://192.168.218.55:9000");
这个操作就是配置HDFS文件的core-site.xml

 

  • 根据配置项获取文件系统
    import org.apache.hadoop.fs.FileSystem;
     
    FileSystem = FileSystem.get(conf);

     

 此时运行程序,控制台显示

说明运行成功,此时可以通过fileSystem实现对HDFS的远程操作。例如:

(1)创建一个目录

Path pm = new Path("/test");
fileSystem.mkdirs(pm);

(2)创建一个文件

Path pf = new Path("/a.txt");
fileSystem.create(pf);

 Notes此处进行操作时有可能报的错误:

        这个错误产生的原因是:在 / 这个目录下只有root用户能进行 rwx 操作,而用本机的idea进行操作时使用的是Windows的当前用户,故需要将该目录设置为:所有用户都可以进行 rwx 操作。 

        还可能产生一个错误:HADOOP_HOME and Hadoop.home.dir are unset. 这个错误是找不到 HADOOP_HOME的配置路径,如果配置没问题但是还是出错的话(有可能是电脑版本型号的问题),可以直接在idea中配置好Hadoop所在目录:

conf.set("hadoop.home.dir", "d:\\software\\hadoop");

 

单元测试

       在编写Java代码的时候,如果想要运行一个Java程序,那么必须创建一个main方法,比较麻烦,比如现在想要测试HDFS的JavaAPI的文件上传 和文件下载 的功能,那如果使用main方法,我们需要创建两个Java类,比较复杂,后期找的时候也比较麻烦。因此在Java中提供了一个工具-----Junit单元测试

       单元测试是属于Java的一个测试方法,最直接的表现形式就是在一个Java文件中可以创建多个“main”方法,如果想要使用单元测试,那么必须引入单元测试的jar包

       单元测试最大的特点就是可以让Java中的普通方法拥有main方法的权利。使用方法:在方法前加入注解:@Test / @Before / @After

@Test:注解就是给Java类的普通方法增加main方法的执行权限,并且在运行的时候只会运行当前的这个方法单元,在一个类中可以存在多个方法单元

@Before:此处补充一个概念:代码块:有一个特点,在执行构造器时先执行代码块。而@Before方法类似于Java中的代码块,@Test单元测试方法执行之前必须先执行@Before修饰的方法中的内容。通常做一些前提准备

@After在@Test修饰的单元测试代码方法执行完成之后,会调用@After修饰的方法。通常做一些销毁工作

 

HDFS的JavaAPI基本操作

Maven依赖准备

 

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <hadoop.version>2.8.5</hadoop.version>
</properties>
 
<!--引入单元测试需要的jar包-->
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.1</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.19</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-common -->
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>${hadoop.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-client -->
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-client</artifactId>
        <version>${hadoop.version}</version>
    </dependency>
</dependencies>

 

 

 

hdfs文件系统的API使用

注意:这些类都在hadoop包下

  • Configuration:HDFS的配置相关类,等同于hadoop中的*-site.xml中的配置。配置文件在代码中采用类似于map集合的key-value键值对的表现形式。配置文件当中配置的配置项执行的优先级要高于我们在Hadoop软件中的配置
  • FileSystem:HDFS文件系统对应的Java类,可以通过这个类获取HDFS上的任何文件和目录

copyFromLocalFile

copyToLocalFile

moveFromLocalFile

moveToLocalFile

rename

delete

mkdirs

listFiles

listStatus

  • Path类:是HDFS上文件在Java中的一个抽象表示,类似于File类
  • FileStatus(LocatedFileStatus:里面包含文件的详情信息(文件路径、文件权限、文件用户、文件修改时间、文件大小等)

操作HDFS:

  • 连接HDFS文件系统------是必备操作(见二、idea连接HDFS

  • 对HDFS进行操作

    • FileSystem
      • 文件上传
              命令:moveFromLocalcopyFromLocalput
              javaAPI
        copyFromLocalFile(Path, Path):复制文件
                               moveFromLocalFile(Path, Path):剪切文件
      • 文件下载
              命令:copyToLocalmoveToLocalget
              JavaAPI
        copyToLocalFile(Path, Path)
                                moveToLocalFile(Path, Path)
      • 文件删除
              命令:-rm -r -rmdir
              JavaAPI
        delete(Path, boolean)
      • 创建文件夹
              命令:mkdir   -p
              JavaAPI
        mkdirs(Path)
      • 更改文件名
              命令:mv 文件路径 更改重命名的文件路径
              JavaAPIrename(Path, Path)
      • 查询某一路径下的文件,可以递归:listFiles
      • 查询某一路径下文件或文件夹,不可以递归:listStatus(常用)
    • FileStatus(LocatedFileStatus):里面包含了文件的详情信息:文件路径、文件权限、文件用户、文件修改时间、文件大小....
    • 案例:
public class HDFSTestDemo {
    private FileSystem fs;
    @Before
    public void init() {
        // hdfs的连接配置
        Configuration conf = new Configuration();
        // 配置hdfs副本数
        conf.set("dfs.replication", "1");
        // 配置hdfs的地址----就是NameNode的地址,NameNode的地址在core-site.xml文件中配置的fs.defaultFS
//        conf.set("fs.defaultFS", "hdfs://192.168.218.55:9000");
        // 获取文件系统
        try {
//            fs = FileSystem.get(conf);
            fs = FileSystem.get(new URI("hdfs://192.168.218.55:9000"), conf, "root");
            System.out.println(fs);
            System.out.println("文件系统获取成功");
        } catch (IOException | URISyntaxException | InterruptedException e) {
            e.printStackTrace();
        }
    }
 
    /**
     * 文件上传测试
     */
    @Test
    public void upload() {
        Path localSrc = new Path("C:\\Users\\15336\\Desktop\\mm.txt");
        Path linuxDe = new Path("hdfs://192.168.218.55:9000/test");
        try {
            fs.copyFromLocalFile(localSrc, linuxDe);
            System.out.println("文件上传成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    /**
     * 文件下载测试
     */
    @Test
    public void download() {
        Path localDst = new Path("E:\\");
        Path linuxSrc = new Path("hdfs://192.168.218.55:9000/test/mm.txt");
        try {
            fs.copyToLocalFile(linuxSrc, localDst);
            System.out.println("文件下载成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    /**
     * 创建目录
     */
    @Test
    public void mkdir() throws IOException {
        boolean mkdirs = fs.mkdirs(new Path("hdfs://192.168.218.55:9000/test/school"));
        if (mkdirs) {
            System.out.println("目录创建成功");
        }
    }
 
    /**
     * 删除目录
     */
    @Test
    public void delete() throws IOException {
        /**
         * 需要传入两个参数
         * @params1: 目录路径
         * @params2: boolean类型的值,代表是否递归删除(如果要删除多层目录,则为true)
         */
        boolean delete = fs.delete(new Path("hdfs://192.168.218.55:9000/school"), false);
        if (delete) {
            System.out.println("删除成功");
        }
    }
 
    /**
     * 重命名或更改文件目录
     */
    @Test
    public void rename() throws IOException {
        boolean rename = fs.rename(new Path("hdfs://192.168.218.55:9000/test/mm.txt"), new Path("hdfs://192.168.218.55:9000/test/vv.txt"));
        if (rename) {
            System.out.println("重命名成功");
        }
    }
 
    /**
     * 查看HDFS文件系统上有哪些文件和文件夹
     */
    @Test
    public  void listFiles() throws IOException {
        /**
         * listFiles 只是查询文件的,目录不能查询
         *      Path:查询的是哪一个路径
         *      boolean:如果这个路径下有一个子文件夹,那么是否把这个子文件夹也遍历查询一下,true是遍历查询,false不是
         *
         *
         * RemoteIterator接口是一个迭代器(有我们查询出来的所有文件信息),它有两个方法:
         *      hasNext():判断有没有下一个文件
         *      next():获取下一个文件
         */
        RemoteIterator<LocatedFileStatus> files = fs.listFiles(new Path("hdfs://192.168.218.55:9000/test"), true);
        while (files.hasNext()) {
            LocatedFileStatus next = files.next();
            System.out.println(next.getPath());
        }
    }
 
    /**
     * 查询hdfs文件系统上的某一文件夹下有哪些文件和文件夹,不能递归
     */
    @Test
    public void listAll() throws IOException {
        /**
         * listStatus(Path) 代表的是查看当前路径下有哪些文件和文件夹
         * 返回值:FileStatus[] 文件状态的一个数组 FileStatus 就代表一个文件的状态:是文件还是文件夹、路径、权限等等
         */
        FileStatus[] fileStatuses = fs.listStatus(new Path("hdfs://192.168.218.55:9000/test"));
        for (FileStatus: fileStatuses) {
            /**
             * fileStatus是一个文件状态对象,里面有很多方法去获取文件的相关信息
             */
            System.out.println("文件路径:" + fileStatus.getPath());
            System.out.println("文件权限:" + fileStatus.getPermission());
            System.out.println("文件所属用户:" + fileStatus.getOwner());
            System.out.println("文件所属组:" + fileStatus.getGroup());
            System.out.println("文件大小:" + fileStatus.getLen());
            System.out.println("文件修改时间:" + fileStatus.getModificationTime());
            System.out.println("文件副本数:" + fileStatus.getReplication());
 
            System.out.println("判断是不是一个文件" + fileStatus.isFile());
            System.out.println("判断是不是一个目录" + fileStatus.isDirectory());
        }
    }
 
    /**
     * 销毁资源
     */
    @After
    public void destroy() {
        if (fs != null) {
            try {
                fs.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

 

递归获取HDFS所有文件

遍历某一个路径下的所有文件和文件夹
*      递归:
*      如果一个文件夹下有文件的话,那么文件的权限直接打印
*      如果是一个文件夹,那么对这个文件夹遍历

public class ListPathAllFile {
    public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException {
        // hadoop的配置文件,执行的优先级最高
        Configuration conf = new Configuration();
        // 连接文件系统
        FileSystem fs = FileSystem.get(new URI("hdfs://192.168.218.55:9000"), conf, "root");
        Path p = new Path("/");
        /**
         * 以前判断一个文件是file还是目录,我们使用的是FileStatus里面的isFile() / isDirectory()
         * FileSystem中也提供了两个方法,用来判断是文件还是文件夹
         *      isFile(Path path)
         *      isDirectory(Path path)
         */
        listStatus(fs, p);
    }
 
    public static void listStatus(FileSystem fs, Path path) throws IOException {
        if (fs.isFile(path)) {
            System.out.println("有一个文件是:" + path.toString());
        } else if (fs.isDirectory(path)) {
            System.out.println("有一个目录:" + path.toString());
            FileStatus[] fileStatuses = fs.listStatus(path);
            for (FileStatus fss: fileStatuses) {
                Path path1 = fss.getPath();
                listStatus(fs, path1);
            }
        }
    }
}

 

FileSystem其他方法

  1. createNewFile(Path)创建一个文件
  2. create(Path):根据HDFS上的一个文件file创建一个文件输出流,然后就可以通过IO流的方式将数据传输到本地
  3. isFile(Path)判断一个文件是不是file
  4. isDirectory(Path)判断一个文件是不是目录(文件夹)
  5. exists(Path)查看路径是否存在
  6. getFileStatus(Path))查看某个文件或者文件夹的状态
  7. createNewFile(Path)创建一个文件

IO流操作HDFS

利用IO流实现文件的上传和下载

  • 使用IO流完成文件的上传

        FileSystem

       create(Path, boolean):根据HDFS文件创建一个文件输出流,可以实现一个流的数据输出到HDFS这个文件系统中   

public void testUpload() throws IOException {
        FileInputStream fis = new FileInputStream(new File("E:\\college_data.txt"));
        // 如果想要通过IO流实现文件的上传,指定overwrite为false,那么hdfs上的文件路径不能提前存在,否则报错
        // 如果指定为true(即追加),则不报错
            FSDataOutputStream = fs.create(new Path("/test/college_data.txt"), false);
        IOUtils.copyBytes(fis, fsDataOutputStream, 1024);
        System.out.println("文件上传完成");
  }

 

  • 使用IO流完成文件的下载
FileSystemopen():用来返回一个文件对应的输入流
public void testIODownload() throws IOException {
    FSDataInputStream fis = fs.open(new Path("/test/d.txt"));
    FileOutputStream fos = new FileOutputStream("E:\\b.txt");
    IOUtils.copyBytes(fis, fos, 1024);
    System.out.println("文件下载成功");
}

利用IO流实现定位文件读取

NotesHDFS存储的文件都是以block块存储的,一个block块默认是128M。假设有一个200M的文件上传到HDFS上,有两个block快,一个是128M,一个是72M(应该有128M,实际占用了72M

使用IO流实现文件的定位读取意思就是比如一个文件分成了两个block快,那么我先下载第一个block块,之后再下载第二个block块

public class IOHDFSTest2 {
    public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException {
        /**
         * 使用IO流完成文件的定位下载
         * 比如一个文件分成了两个block快,那么我先下载第一个block块,之后再下载第二个block块
         */
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(new URI("hdfs://192.168.218.55:9000"), conf, "root");
        FSDataInputStream open = fs.open(new Path("/test/hadoop-2.8.5.tar.gz"));
        /**
         * 下载第一个block块:128M
         */
        FileOutputStream fos = new FileOutputStream("E:\\part1.tar.gz");
        byte[] buf = new byte[1024];
        for (int i = 0; i < 128 * 1024; i++) {
            open.read(buf);
            fos.write(buf);
 
        }
 
        /**
         * 下载第二个block块:从128M开始
         */
        FileOutputStream fos1 = new FileOutputStream("E:\\part2.tar.gz");
        /**
         * 定位到128M的位置,定位之后只用IO流开始下载
         */
        // 定位偏移量
        open.seek(128 * 1024 * 1024);
        IOUtils.copyBytes(open, fos1, 1024);
    }
}

 

 
posted @ 2021-07-29 16:14  张涨涨  阅读(3303)  评论(0编辑  收藏  举报
Live2D