HDFS的Java Api操作【获取Hdfs文件系统、遍历、创建文件和文件夹、上传、下载、权限访问控制、合并上传或下载】

1.使用url方式访问数据(了解)

    @Test
    public void urlHdfs() throws IOException {
    
    

        //1:注册url
        URL.setURLStreamHandlerFactory( new FsUrlStreamHandlerFactory());
        //2:获取hdfs文件的输入流
        InputStream inputStream = new URL("hdfs://node01:8020/a.txt").openStream();

        //3:获取本地文件的输出流
        FileOutputStream outputStream = new FileOutputStream(new File("D:\\hello2.txt"));

        //4:实现文件的拷贝
        IOUtils.copy(inputStream, outputStream);

        //5:关流
        IOUtils.closeQuietly(inputStream);
        IOUtils.closeQuietly(outputStream);

    }

2.使用文件系统方式访问数据(掌握)

(1)涉及的主要类

在Java中操作HDFS,主要涉及以下class:

  • Configuration
    • 该类的对象封装了客户端或者服务器的配置
  • FileSystem
    • 该类的对象是一个文件系统对象,可以用该对象的一些方法来对文件进行操作,通过FileSystem的静态方法get获得该对象。

FileSystem fs = FileSystem.get(conf)

  • get方法从conf中的一个参数fs.defaultFS的配置值判断具体是什么类型的文件系统
  • 如果我们的代码中没有指定fs.defaultFS,并且工程ClassPath下也没有给定相应的配置,conf中的默认值就来自于Hadoop的Jar包中的core-default.xml
  • 默认值为file:/// ,则获取的不是一个DistributedFileSystem的实例,而是一个本地文件系统的客户端对象

(2)获取FileSystem的几种方式

a. 第一种方式

在这里插入图片描述

   /*
      获取FileSystem;方式1
     */
    @Test
    public void getFileSystem1() throws IOException {
    
    
        //1:创建Configuration对象
        Configuration configuration = new Configuration();

        //2:设置文件系统的类型
        configuration.set("fs.defaultFS", "hdfs://node01:8020");

        //3:获取指定的文件系统
        FileSystem fileSystem = FileSystem.get(configuration);

        //4:输出
        System.out.println(fileSystem);
    }

b. 斜体样式第二种方式

在这里插入图片描述

     /*
      获取FileSystem;方式2
     */
     @Test
     public void getFileSystem2() throws URISyntaxException, IOException {
    
    
         FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());

         System.out.println(fileSystem);
     }

c. 第三种方式

在这里插入图片描述

    /*
    获取FileSystem;方式3
   */
    @Test
    public void getFileSystem3() throws IOException {
    
    
        Configuration configuration = new Configuration();
        //指定文件系统类型
        configuration.set("fs.defaultFS", "hdfs://node01:8020");

        //获取指定的文件系统
        FileSystem fileSystem = FileSystem.newInstance(configuration);

        System.out.println(fileSystem);
    }

d. 第四种方式

在这里插入图片描述

    /*
    获取FileSystem;方式4
    */
    @Test
    public void getFileSystem4() throws URISyntaxException, IOException {
    
    
        FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://node01:8020"), new Configuration());

        System.out.println(fileSystem);

    }


3.遍历HDFS中所有文件

使用API遍历

    /*
     hdfs文件的遍历
     */
    @Test
    public void listFiles() throws URISyntaxException, IOException {
    
    
        //1:获取FileSystem实例
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());

        //2:调用方法listFiles 获取 /目录下所有的文件信息,,参数true代表递归遍历
        RemoteIterator<LocatedFileStatus> iterator = fileSystem.listFiles(new Path("/"), true);

        //3:遍历迭代器
        while (iterator.hasNext()){
    
    
            LocatedFileStatus fileStatus = iterator.next();

            //获取文件的绝对路径 : hdfs://node01:8020/xxx
            //getPath()方法就是获取绝对路径
            System.out.println(fileStatus.getPath() + "----" +fileStatus.getPath().getName());

            //文件的block信息
            BlockLocation[] blockLocations = fileStatus.getBlockLocations();
            System.out.println("block数:"+blockLocations.length);
        }
    }

4.HDFS上创建文件夹

	/*
        hdfs创建文件夹
     */
    @Test
    public void mkdirsTest() throws URISyntaxException, IOException {
    
    
        //1:获取FileSystem实例
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());

        //2:创建文件夹
        //boolean bl = fileSystem.mkdirs(new Path("/aaa/bbb/ccc"));

        //创建文件
        fileSystem.create(new Path("/aaa/bbb/ccc/a.txt"));
        //如果父目录不存在,则会一并创建
        fileSystem.create(new Path("/aaa2/bbb/ccc/a.txt"));
        //System.out.println(bl);

        //3: 关闭FileSystem
        //fileSystem.close();

    }

5.HDFS文件下载

方式1:

     /*
      实现文件的下载
     */
    @Test
    public void downloadFile() throws URISyntaxException, IOException {
    
    
        //1:获取FileSystem
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());

        //2:获取hdfs的输入流,,注意获取的HDFS文件系统上的路径
        FSDataInputStream inputStream = fileSystem.open(new Path("/a.txt"));

        //3:获取本地路径的输出流,,注意这里获取的是本地磁盘路径
        FileOutputStream outputStream = new FileOutputStream("D://a.txt");

        //4:文件的拷贝
        IOUtils.copy(inputStream, outputStream);
        //5:关闭流
        IOUtils.closeQuietly(inputStream);
        IOUtils.closeQuietly(outputStream);
        fileSystem.close();
    }

方式2:

通过对比方式1,可以使用copyToLocalFile方法一步到位。

    /*
    实现文件的下载:方式2
     */
    @Test
    public void downloadFile2() throws URISyntaxException, IOException, InterruptedException {
    
    
        //1:获取FileSystem
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration(),"root");
        //2:调用方法,实现文件的下载

        fileSystem.copyToLocalFile(new Path("/a.txt"), new Path("D://a4.txt"));

        //3:关闭FileSystem
        fileSystem.close();
    }

6.文件上传

和文件下载格式一样,,不同的是方法不同,
文件上传使用copyFromLocalFile方法一步到位。

注意:

扫描二维码关注公众号,回复: 13304708 查看本文章
  • copyFromLocalFile方法的中的俩个路径参数和文件下载中的copyToLocalFile方法中的路径参数相反,,,就是上传和下载的区别,,很好理解。。。
    /*
     文件的上传
     */
    @Test
    public void uploadFile() throws URISyntaxException, IOException {
    
    
        //1:获取FileSystem
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());

        //2:调用方法,实现上传
        fileSystem.copyFromLocalFile(new Path("D://set.xml"), new Path("/"));

        //3:关闭FileSystem
        fileSystem.close();


    }


7.HDFS的权限访问控制

(1)通过配置文件进行权限控制

在这里插入图片描述
注意查看该文件的编辑内容, 默认为 false ,, 就是说让所有权限自身的作用失效,,也就是说在hdfs上显示的rwx权限没有任何意义。 所以要想修改文件的权限,就得修改为true
在这里插入图片描述
接下来我们验证上面参数的作用

首先通过命令来修改hdfs系统上的a.txt文件权限

hdfs dfs -chmod 000 /a.txt

结果如下
在这里插入图片描述

然后我们通过Java Api 来下载该文件到本地磁盘D盘下

@Test
    public void mkdirsTest() throws URISyntaxException, IOException {
    
    
        //1:获取FileSystem实例
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.217.128:9000"), new Configuration());

        //2:调用方法,实现文件的下载

        fileSystem.copyToLocalFile(new Path("/a.txt"), new Path("D://a2.txt"));

        //3:关闭FileSystem
        fileSystem.close();
    }

运行后发现在本地D盘下有a2.txt文件,
在这里插入图片描述
通过测试,发现即使该文件的权限被改为000,结果还是可以被访问,然后被下载到本地,,所以说,,,httpfs-site.xml文件中该部分参数为false的话,就是让所有权自身的作用失效

注意:如果要将该参数改为true,则首先必须将hdfs集群关闭,不能在运行状态下修改配置文件

下面我们继续进行验证,,这次先在Hadoop主目录下的sbin目录关闭hdfs文件系统,通过如下命令

sbin/stop-dfs.sh

在这里插入图片描述

注意:
这里如果将master主节点上的配置文件改为true的话,也必须修改其他节点的配置文件。

然后通过分发命令,进行分发:

scp hdfs-site.xml slave1:$PWD
scp hdfs-site.xml slave2:$PWD

在这里插入图片描述
然后我们再次启动hdfs集群

sbin/start-all.sh

在这里插入图片描述

然后我们使用Java Api 再次进行下载文件,,会发生如下报错,,应为此时我们将配置文件中的参数修改为true,就是使得所有的文件权限生效。所以再次下载访问时,回报如下错误。

在这里插入图片描述

由于权限rwx作用全部生效,,所以我们如果还想通过Java Api进行下载的话,就必须得修改该文件的权限。 同时一定要注意该文件的所属用户!!!

hdfs dfs -chmod 666 /a.txt

在这里插入图片描述
此时便能通过Java Api 下载到本地
在这里插入图片描述

(2)通过伪装用户进行越权

如果想要不修改该文件的权限就下载访问该文件,就可以通过伪装用户的方式,来进行下载。

就是在Java代码中的get方法中多添加一个参数,该参数则是要伪装的用户名字。例如下面代码:

    @Test
    public void mkdirsTest() throws URISyntaxException, IOException, InterruptedException {
    
    
        //1:获取FileSystem实例
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.217.128:9000"), new Configuration(),"root");

        //2:调用方法,实现文件的下载

        fileSystem.copyToLocalFile(new Path("/a.txt"), new Path("D://a4.txt"));

        //3:关闭FileSystem
        fileSystem.close();
    }

如下图,本地有a4.txt文件,,同样可以执行成功!!!
在这里插入图片描述



小文件合并

由于Hadoop擅长储存大文件,因为大文件的元数据信息比较少,如果Hadoop集群中有大量的小文件,那么每个小文件都需要维护一份元数据信息,会大大的增加集群管理元数据的内存压力,所以在实际工作当中,如果有必要一定要将小文件合并成为大文件进行一起处理。

(1)合并下载

在我们的HDFS的Shell命令模式下,可以通过命令行将很多hdfs文件合并成一个大文件下载到本地。

例如我们将hdfs根目录下的xml文件合并成hello.xml文件并且下载到本地:

hdfs dfs -getmerge /*.xml ./hello.xml

在这里插入图片描述
在这里插入图片描述
如上图所示,本地hello.xml文件的内容则是core-site.xml文件和hdfs-site.xml文件内容的合并

(2)合并上传

既然可以在下载的时候将这些小文件合并成一个大文件下载,那么就能在本地合并小文件进行上传。

    /*
      小文件的合并
     */
    @Test
    public void mergeFile() throws URISyntaxException, IOException, InterruptedException {
    
    
        //1:获取FileSystem(分布式文件系统),,伪装用户root
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration(),"root");

        //2:获取hdfs大文件的输出流
        FSDataOutputStream outputStream = fileSystem.create(new Path("/big_txt.txt"));

        //3:获取一个本地文件系统
        LocalFileSystem localFileSystem = FileSystem.getLocal(new Configuration());

        //4:获取本地文件夹下所有文件的详情
        FileStatus[] fileStatuses = localFileSystem.listStatus(new Path("D:\\input"));
        
        //5:遍历每个文件,获取每个文件的输入流
        for (FileStatus fileStatus : fileStatuses) {
    
    
            FSDataInputStream inputStream = localFileSystem.open(fileStatus.getPath());

            //6:将小文件的数据复制到大文件
            IOUtils.copy(inputStream, outputStream);
            IOUtils.closeQuietly(inputStream);
        }

        //7:关闭流
        IOUtils.closeQuietly(outputStream);
        localFileSystem.close();
        fileSystem.close();
    }

猜你喜欢

转载自blog.csdn.net/shuyv/article/details/110391099