java客户端连接hdfs实现简单功能-06

完全分布式部署Hadoop-04https://blog.csdn.net/kxj19980524/article/details/88954645

上面是hadoop集群的搭建

连接客户端前先下载相应的jar包,和hadoop编译过的源码

win10

链接:https://pan.baidu.com/s/1yXfyBesrUeZuR-KPPUG1eQ 
提取码:mnvl 
 

win7

链接:https://pan.baidu.com/s/1dW9L3F6pKNPtU9b3EIcRJA 
提取码:9wl0 

下载好相应版本的jar包后,配置环境变量,下载好的包放到一个无中文目录下,lib里面是客户端需要的jar包,下面是代码.

配置环境变量和path路径,跟java一样

然后创建一个java项目,把lib下面的jar包,除了source和tests外,剩下的包都导入项目中

在hosts中配置好跟集群相对应的信息

复制本地文件到hdfs的案例

package com.buba.hdfs;


import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

import java.net.URI;

public class HDFSClient {
    public static void main(String[] args)throws Exception {
       
        //0获取配置信息
        Configuration configuration = new Configuration();
        configuration.set("fs.defaultFS","hdfs://hadoop102:8020");

        //1获取文件系统
        FileSystem fileSystem = FileSystem.get(configuration);
       
        //2拷贝本地数据到集群
        fileSystem.copyFromLocalFile(new Path("F:\\kxj.txt"),new Path("/user/kxj/kxj3.txt"));

        //3.关闭文件系统
        fileSystem.close();
    }
}
 

如果这样直接启动的话会报错,说没有用户名,没有权限访问hadoop集群,需要带参数运行

-DHADOOP_USER_NAME=kxj   指定hadoop集群上的用户.然后再运行就可以了

第二种种写发就无需添加参数,使用FileSystem的另一个有参构造函数.

package com.buba.hdfs;


import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

import java.net.URI;

public class HDFSClient {
    public static void main(String[] args)throws Exception {
        //-DHADOOP_USER_NAME=kxj
        //0获取配置信息
        Configuration configuration = new Configuration();
        //configuration.set("fs.defaultFS","hdfs://hadoop102:8020");

        //1获取文件系统
        //FileSystem fileSystem = FileSystem.get(configuration);
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration, "kxj");
        //2拷贝本地数据到集群
        fileSystem.copyFromLocalFile(new Path("F:\\kxj.txt"),new Path("/user/kxj/kxj3.txt"));

        //3.关闭文件系统
        fileSystem.close();
    }
}
 

第三种获取文件系统方式,把core-site.xml配置文件放项目下

也是可以获取到文件系统的,只不过ugi变为了Admin,但是想要执行copy之类的操作的话就会报错,因为集群搭建的时候是使用的kxj用户名.

这三种获取文件系统的优先级是 :java代码最高>java项目配置文件>远程linux的hadoop配置文件

    @Test
    public void getFileSystem()throws Exception{
        Configuration configuration = new Configuration();
        FileSystem fileSystem = FileSystem.get(configuration);
        System.out.println(fileSystem);
        //fileSystem.copyFromLocalFile(new Path("F:\\kxj.txt"),new Path("/user/kxj/kxj4.txt"));
    }

其它对hdfs的操作都使用FileSystem这个对象来操作,调它里面的api就可以了.

文件上传:

参数1是否删除本地文件true为删除  参数2 要上传的文件 参数3 上传的位置,不加第一个参数默认为false

fileSystem.copyFromLocalFile(true,new Path("F:\\kxj.txt"),new Path("/user/kxj/kxj3.txt"));

文件下载:

三个重载方法,第一个布尔值还是是否删除原文件,最后一个布尔值,是否进行校验,如果从hdfs下载下来的东西没内容就加上校验.

fileSystem.copyToLocalFile(new Path("/user/kxj/output/kxj.txt"),new Path("F:\\kxj.txt"));

创建目录:

第二个参数是给权限,这两个方法可以直接创建多级目录

fileSystem.mkdirs(new Path("/kxj/user"));

删除目录:

fileSystem.delete(new Path("/kxj"),true);

重命名:

参数1:旧名称  参数2:新名称

fileSystem.rename(new Path("/user/kxj/kxj.txt"),new Path("ch.txt"));

文件的详细信息: 里面还有其它信息

RemoteIterator<LocatedFileStatus> remoteIterator = fileSystem.listFiles(new Path("/"), true);
        while (remoteIterator.hasNext()){
            LocatedFileStatus next = remoteIterator.next();
            //文件名称
            System.out.println(next.getPath().getName());
            //块大小
            System.out.println(next.getBlockSize());
            //文件长度
            System.out.println(next.getLen());
            //权限
            System.out.println(next.getPermission());

            BlockLocation[] blockLocations = next.getBlockLocations();

            //存储块的详细信息
            for(BlockLocation b:blockLocations){

                //从哪个块开始存储
                System.out.println(b.getOffset());
                //拥有者
                String[] hosts = b.getHosts();
                for(String s:hosts){
                    System.out.println(s);
                }

            }
            System.out.println("----------------");
        }

 判断一个目录下的所有文件是文件夹还是文件,没有递归功能,只能在那一级目录下

        FileStatus[] listStatus = fileSystem.listStatus(new Path("/"));

        // 3 遍历所有文件状态
        for (FileStatus status : listStatus) {
            if (status.isFile()) {
                //是文件
                System.out.println("f--" + status.getPath().getName());
            } else {
                //是文件夹
                System.out.println("d--" + status.getPath().getName());
            }
        }

使用IO流的方式进行上传文件 

看下图,相对于java程序来说,本地文件传到java程序种属于输入流,java程序上传到hdfs属于输出流

@Test
    public void putFileToHDFS() throws Exception{
        Configuration configuration = new Configuration();
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration, "kxj");

        //  创建输入流
        FileInputStream inStream = new FileInputStream(new File("F:/hello.txt"));
        
        //  创建输出流
        FSDataOutputStream outStream = fileSystem.create(new Path("/user/kxj/input/hello.txt"));

        // 流对接
        try{
            IOUtils.copyBytes(inStream, outStream, configuration);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            IOUtils.closeStream(inStream);
            IOUtils.closeStream(outStream);
        }
    }

 使用IO流的方式进行文件下载,思路跟上面正好相反,输入输出反了一下,调用open方法获取输入流

@Test
    public void getFileToHDFS() throws Exception{
        Configuration configuration = new Configuration();
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration, "kxj");

        //  创建输出流
        FileOutputStream fileOutputStream = new FileOutputStream(new File("F:/hello.txt"));

        //  创建输入流
        FSDataInputStream open = fileSystem.open(new Path("/user/kxj/input/hello.txt"));

        // 流对接
        try{
            IOUtils.copyBytes(open, fileOutputStream, configuration);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            IOUtils.closeStream(open);
            IOUtils.closeStream(fileOutputStream);
        }
    }

定位文件获取

之前都是下载的单块的数据,以前上传到hdfs上的hadoop安装包份了两块共185兆左右,那怎么把它完全的下载下来呢?就需要份块下载了,第一次下载128兆,因为一个block为128兆,分两次下载,然后拼接到一块.1024byte=1kb 

@Test
    // 定位下载第一块内容
    public void readFileSeek1() throws Exception {

        // 创建配置信息对象
        Configuration configuration = new Configuration();

        FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration, "kxj");

        //  获取输入流路径

        FSDataInputStream fis = fs.open(new Path("hdfs://hadoop102:8020/user/kxj/input/hadoop-2.7.2.tar.gz"));

        //  创建输出流
        FileOutputStream fos = new FileOutputStream("F:/hadoop-2.7.2.tar.gz.part1");

        //  流对接    1024byte是1Kb  1024*1024=1Mb  一个block128Mb
        byte[] buf = new byte[1024];
        for (int i = 0; i < 128 * 1024; i++) {
            fis.read(buf);
            fos.write(buf);
        }

        // 关闭流
        IOUtils.closeStream(fis);
        IOUtils.closeStream(fos);
    }

使用seek方法定位到第二块的开头

@Test
    // 定位下载第二块内容
    public void readFileSeek2() throws Exception {

        // 创建配置信息对象
        Configuration configuration = new Configuration();

        FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration, "kxj");

        //  获取输入流路径

        FSDataInputStream fis = fs.open(new Path("hdfs://hadoop102:8020/user/kxj/input/hadoop-2.7.2.tar.gz"));

        //  创建输出流
        FileOutputStream fos = new FileOutputStream("F:/hadoop-2.7.2.tar.gz.part2");

        //  定位偏移量(第二块的首位)
       fis.seek(1024*1024*128);

        //流拼接
        IOUtils.copyBytes(fis,fos,configuration);

        // 关闭流
        IOUtils.closeStream(fis);
        IOUtils.closeStream(fos);
    }

然后使用cmd命令把第二块拼接到第一块里面,可以看到大小明显改变了

type hadoop-2.7.2.tar.gz.part2>> hadoop-2.7.2.tar.gz.part1

然后把part1重命名,就可以解压了,可以看到已经拼接好了

定位文件获取的作用,有时候海量数据的时候,不需要把全部数据下载下来,只需要其中的一块,这时候就需要定位了.

 

保证数据一致性问题

一个客户端上传数据原先版本为10,三个副本也都是10,然后他又改变数据了,改为11了,副本同步是需要时间的,不是时时的,在这期间另一个客户端读数据,离节点3比较近,那他读取的就是旧数据,假如你这数据非常重要需要时时变化,这该怎么办呢?

调用 hflush ();           //清理客户端缓冲区数据,被其他client立即可见,调用它的时候底层会实现锁使其它客户端访问不了,这就会影响效率,看情况而定需要的时候再加.

@Test
	public void writeFile() throws Exception{
		// 1 创建配置信息对象
		Configuration configuration = new Configuration();
		fs = FileSystem.get(configuration);
		
		// 2 创建文件输出流
		Path path = new Path("hdfs://hadoop102:9000/user/atguigu/hello.txt");
		FSDataOutputStream fos = fs.create(path);
		
		// 3 写数据
		fos.write("hello".getBytes());
        // 4 一致性刷新
		fos.hflush();
		
		fos.close();
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值