Hadoop学习笔记和总结(六)

目录

第六章 HDFS的命令操作

6.1 HDFS命令方式

6.1.1 HDFS常用指令

6.1.2 其他命令

6.2 Java API的方式(HDFS的API)

6.2.1 导入Jar包

6.2.2 使用Java API

6.2.3 HDFS中常用的API

6.2.4 I/O流操作HDFS


第六章 HDFS的命令操作

HDFS实质就是一个文件系统,有两种操作方式:HDFS命令方式Java API的方式。HDFS的命令方式有些类似于Linux的操作方式。本章主要针对HDFS的命令方式进行说明。

6.1 HDFS命令方式

在使用命令方式操作HDFS之前,应先开启Hadoop或HDFS客户端。特别地,HDFS中访问文件只有一种方式,即只能通过绝对路径进行访问。其中,/ 代表的抽象根目录,其具体表示为:hdfs://master:9000/xxx

6.1.1 HDFS常用指令

hadoop fs :开启原生文件系统客户端(里面有指令提示)

hadoop fs -ls :查看根目录下的文件

hadoop fs -mkdir -p  /aa/bb/cc/dd :循环建立文件夹

HDFS的核心功能—上传和下载,其两个核心的命令分别为:

(1)文件上传:从本地上传至HDFS,这里的本地文件目录是指Linux下的文件目录

hadoop fs -put  [-f]  [-p]  [-l]  <localsrc> ... <dst>

hadoop fs -put   本地文件目录    HDFS上的目录

例如:hadoop  fs   -put  1.txt  2.txt  /ss :上传时,会把最后一个路径之前的目录看做全部要上传的文件。

(2)文件下载:从HDFS下载到本地,即下载到Linux下的目录中

hadoop fs -get [-p] [-ignoreCrc] [-crc] <src> ... <localdst>

hadoop fs -get  HDFS路径   本地路径

(3)合并下载:HDFS下载到本地目录,并将两个文件合并成一个文件

hadoop fs  -getmerge   /ss/1.txt   /ss/2.txt   /home/hadoop/cc.txt

该指令会把最后一个路径之前的文件当做需要合并的文件,最后一个路径的文件就是合并后的文件。

查看文件:hadoop fs -cat

删除文件:hadoop fs  -rm -r(递归删除)-f(强制,不提示)

移动文件:hadoop fs  -mv(修改名字或移动文件)

复制文件:hadoop fs  -cp

以上这些命令,在集群中的任意一个节点都可以使用。因为在HDFS中我们所看到的目录结构只是一个抽象目录树,实际存储是在集群中的节点上。

如:现将一个大小为150M,名字为aa.txt的文件上传至HDFS的根目录下,执行hadoop fs -put aa.txt / 后,在根目录下会看到/aa.txt。

但是aa.txt在真实存储的时候会先进行分块(分两块),再进行存储。假设集群中有5个DataNode,这两个块由NameNode分配存储在哪个节点中。

以上都是常用命令

6.1.2 其他命令

文件追加:hadoop fs -appendToFile  本地文件  HDFS文件

(追加方式:在文件的末尾追加;若追加后文件大小超过128M,会自动进行切分成块)

文件显示末尾:hadoop  fs  -tail

Linux用到的监听命令:tail -f  文件路径 (-f 代表跟踪)

修改权限:就比Linux系统多了个fs

修改文件或目录的权限:hadoop  fs    -chomd  -R   777   hdfs目录/..

修改文件或目录的所属租:hadoop  fs    -chown  -R   用户名:组名    hdfs目录/文件

(p.s)Linux下的权限管理命令:

(1)修改文件、目录的权限:chmod  文件权限

文件权限包括3种:可读、可写、可执行。对应的字母分别为:r、w、x;对应的数字分别为:4、2、1

文件的最大权限是:可读可写可执行;即rwx或7

例如:

红框中-rw-r--r--,一共10位。

第1位:代表文件属性,d为目录,-为文件,l为连接

其余9位,三个为一组。分别代表本用户,本组用户和其他用户的权限。

结合图,rw- 本用户,6;rw- 本组用户,4;r-- 其他用户,4

修改目录或文件的权限

Chmod  711 文件名

改一个文件夹下的所有的文件权限都是711(可读可写,可运行,可运行)

(2)修改文件所属用户和组:

chown   -R   用户名:组名   文件/目录

sodu  chown   -R   root:root   /ss

6.2 Java API的方式(HDFS的API)

使用API操作HDFS的前提是:Hadoop集群可用;然后安装配置eclipse插件,通过eclipse插件连接HDFS。

6.2.1 导入Jar包

导入Jar包通常有三种方式:

(1)在工程中建立一个lib文件夹,添加Jar包后build to path

优点:代码移动时比较方便,不用重新导包

缺点:不好解决Jar包冲突,易造成工程过于臃肿

(2)使用maven管理Jar包(企业常用)

优点:解决了Jar包冲突问题

缺点:代码移动时,需要重构工程

(3)手动建立一个依赖库

以上三种方式自己在开发中都可以使用,可根据不同特点进行选择。

本人在学习中使用的Hadoop版本是Hadoop2.7.5,找到该文件的安装目录-->D:\hadoop\hadoop-2.7.5;各模块的Jar包都存放在一个share文件夹中:D:\hadoop\hadoop-2.7.5\share\hadoop;

操作HDFS需要使用的Jar包在common和hdfs文件夹中,我们分别将这两个文件夹中的所有Jar包导入工程,如下图所示:

6.2.2 使用Java API

(1)FileSystem对象

该对象是HDFS抽象目录树的一个实例,如果用户想要操作HDFS,首先需要获取到这个实例。

(ps:Java中有5种方式获取实例:new、利用反射、利用工厂类、单例设计模式的静态方法和克隆。)

FileSystem对象包括单机模式的FileSystem对象分布式模式的FileSystem对象两种,接下来分别进行说明:

1)单机模式的FileSystem

单机模式下的FileSystem对象是本地文件系统抽象目录树的一个实例,这个本地指代码的运行地;因为代码运行在Windows中,所以本地文件系统的根目录是windows下,Hadoop安装目录中磁盘的根目录。若此时使用HDFS中的上传功能,将文件进行上传。开发者本想将程序上传至HDFS,实际上却上传到了本地(即windows中的磁盘)。因为本人将Hadoop安装在了D盘中,所以单机模式FileSystem的根目录则是D盘。

下面是具体测试代码:

package com.ethan.hdfs;

import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

/**
 * 单机模式下的FileSystem
 * @author Ethan
 *
 */
public class HdfsLocalTest {

	public static void main(String[] args) {
		
// FileSystem:这个对象是HDFS抽象目录树的一个实例,如果用户想要操作HDFS,首先需要获取到这个实例
		// 获取配置文件:哪里的?
		Configuration conf = new Configuration();
	try {
		// 单机模式的文件系统抽象目录树实例
		// 根目录 --> file:\\\D:.....
		FileSystem fs = FileSystem.get(conf);  // 获取本地文件系统
		// 这个本地指的是代码的运行地
		System.out.println(fs);
		// org.apache.hadoop.fs.LocalFileSystem@31368b99 本地文件系统
		
		// Path hdfs内置对象 代表文件路径对象
		Path src = new Path("磁盘中文件的绝对路径。。。");
		Path dst = new Path("/");  // 根目录。。。希望是HDFS上的根目录,但却是本地系统的根目录
		
		// 想上传至HDFS,实际是本地
        fs.copyFromLocalFile(src, dst);
		
	} catch (IOException e) {
		e.printStackTrace();
	}
	}
}

2)分布式模式的FileSystem对象

在获取FileSystem实例时,选择带URI参数的get方法即可。注意:最好选择get(URI uri, Configuration conf, String user)方法,URI:HDFS的IP和端口号;conf:配置文件实例;user:集群用户名。若不选择该方法,容易报windows用户无权限错误。

下面是具体测试代码:

package com.ethan.hdfs;

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

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


/**
 * 分布式模式下的FileSystem
 * @author Ethan
 *
 */
public class HdfsClusterTest {

	public static void main(String[] args) {
		
	  Configuration conf = new Configuration();

	        // 这时候的根目录代表HDFS,根目录--> hdfs://master:9000
		try {
			// 以指定获取文件系统的url连接,完全分布式的url;core-site.xml中fs.default
			// 分布式模式的文件系统抽象目录树实例 
			FileSystem fs = FileSystem.get(new URI("hdfs://xxx.xxx.xxx.xx:9000"), conf, "root");
			
            if (fs instanceof DistributedFileSystem) {
				System.out.println("fs是分布式模式下抽象目录树的实例。。。");
			}else {
				System.out.println("fs不是分布式模式下抽象目录树的实例。。。");
			}
			Path src = new Path("想上传文件的目录。。。");
			Path dst = new Path("/");
			
			fs.copyFromLocalFile(src, dst);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
	}
}

(2)Configuration对象

该对象是加载配置文件的对象,即该对象用于读取配置文件。

读取的配置文件包括:core-default.xml、hdfs-default.xml、mappred-default.xml和yarn-default.xml

配置文件加载(读取)的顺序:

a)首先加载Jar包中的xxx-default.xml

b)  然后加载工程classpath(src)下的配置文件

并且只识别两种名字,hdfs-default.xml和hdfs-site.xml,其他名字识别不了

c) 通过代码去设置配置项

例如:

Configuration conf = new Configuration();
conf.set("dfs.replication", "5");

如果这这三种方式全都配置,应该以哪个配置文件为准呢?实际上,根据加载(读取)的优先级:Jar > src > 代码配置即可得出答案:哪个是最后一次设置或加载的,哪个配置就是生效的

6.2.3 HDFS中常用的API

下面列举一些常用的FileSystem操作HDFS的方法:

package com.ethan.hdfs;

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

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.BlockLocation;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.LocatedFileStatus;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.RemoteIterator;

/**
 * HDFS中常用的API
 * @author Ethan
 *
 */
public class CommonApi {

	public static void main(String[] args) {

		Configuration conf = new Configuration();
		 
		try {
			
			FileSystem fs = FileSystem.get(new URI("hdfs://xxx.xxx.xx.xxx:9000"), conf, "root");
			
			Path path = new Path("/api_test/aa/bb");
			// 创建文件夹,可递归创建文件夹
			fs.mkdirs(path); // 创建成功返回true,失败返回false
			
			// 可以设定是否递归删除
			fs.delete(new Path("/api_test/aa/bb"), false); // true就是递归删除
			
			// 上传文件
			fs.copyFromLocalFile(src, dst);
			//下载文件
			fs.copyToLocalFile(src, dst);
			
			// 判断目录是否存在
			fs.exists();
			
			// 对文件进行重命名
			fs.rename(src, dst);
			
			// 获取指定目录下的文件列表(只能获取文件列表)
			RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/hdfs_test"), false); // 文件的迭代器
			// 循环遍历迭代器
			while(listFiles.hasNext()) {
				// 获取下一个
				LocatedFileStatus next =  listFiles.next();
				System.out.println(next.getPath());
				System.out.println(next.getReplication());
				System.out.println(next.getBlockSize());
				
				// 返回每一个文件的块信息,结果封装在数组中,数据长度代表块的个数,每一个块代表数组中的一个元素
				BlockLocation[] blockLocations = next.getBlockLocations();
				System.out.println(blockLocations.length);

				// bl代表每一个文件的每一个块的信息
				for (BlockLocation bl : blockLocations) {
					// 0,36,master,slave2,slave1 :起始偏移量,末尾偏移量,块的存储位置
					System.out.println(bl + "\t");
					
					// 返回的是获取每一个块的存储节点
					String[] hosts = bl.getHosts();
					
					long offset = bl.getOffset();
					for (String host : hosts) {
						System.out.println(host);
					}
				}
			}
			
			// 返回指定目录下的文件或目录状态信息
			FileStatus[] fileStatus = fs.listStatus(new Path("/"));
			for (FileStatus f : fileStatus) {

				// 判断是文件还是文件夹
				System.out.println(f.isDirectory());
				System.out.println(f.isFile());
			}
			// 关闭流
			fs.close();			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}
}

在此具体说明一下由FileSystem上传文件至HDFS时,文件名的问题。

(1)在上传文件时,若没有指定文件名,并且上传的父目录存在,则以原文件名存储;若指定了文件名,则上传的文件名为指定的文件名。测试如下:

package com.ethan.hdfs;

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

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

public class HdfsParctise {

	public static void main(String[] args) {

		Configuration conf = new Configuration();
		
		try {
			
			FileSystem fs = FileSystem.get(new URI("hdfs://192.168.56.110:9000"), conf, "root");
			
			Path localPath = new Path("D:\\z_hadooptest\\20200114.txt");
			Path dirPath = new Path("/hdfs_test");
			Path dirPathUseName = new Path("/hdfs_test/20200114useName.txt");
			
			// 未指定文件名上传
			fs.copyFromLocalFile(localPath, dirPath);
			
			// 指定文件名上传
			fs.copyFromLocalFile(localPath, dirPathUseName);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}
}

上传后的结果:

(2)上传文件时,如果上传的路径已经存在,则文件最终上传到该目录下;如果不存在,并未指定文件名,则直接以上传目录的最后一级目录作为文件名;若指定文件名,则自动在HDFS中递归建立文件夹,并以指定的文件名上传文件;

package com.ethan.hdfs;

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

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

public class HdfsParctise {

	public static void main(String[] args) {

		Configuration conf = new Configuration();
		
		try {
			
			FileSystem fs = FileSystem.get(new URI("hdfs://xxx.xxx.xx.xxx:9000"), conf, "root");
			Path localPath = new Path("D:\\z_hadooptest\\20200114.txt");

			Path dirNoExistNoName = new Path("/ethan/shadow_knight");

            Path dirNoExistByName = new Path("/bond/007/20200114NoExist.txt");

			// 路径不存在,上传
			fs.copyFromLocalFile(localPath, dirNoExistNoName);

            // 路径不存在,但指定了文件名,上传
			fs.copyFromLocalFile(localPath, dirNoExistByName);

            
            // 上传结果:由于/ethan/shadow_knight路径不存在,最终文件名为shadow_knight
           // 由于/bond/007/20200114NoExist.txt路径不存在,但指定了文件名,最终文件名为20200114NoExist.txt
        
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}
}

上传后的结果:

6.2.4 I/O流操作HDFS

开发人员也可以用I/O流操作HDFS,进行文件的上传和下载。

(1)文件上传

从本地上传至HDFS,实际上是从本地读取文件,写到HDFS。对应的I/O流为:本地(输入流)、HDFS(输出流);具体来说,本地的输入流是普通的输入流,HDFS的输出流为HDFS专有的输出流

文件上传步骤如下:

1)创建本地输入流:FileInputStream in = new FileInputStream(new File("D:\\xxxx.txt"));

2)创建HDFS输出流:FSDataOutputStream out = fs.create(new Path("/hdfs_test/xxx.txt"));

ps:创建流时必须指定一个文件名。此处与使用API方式操作HDFS不一样了。

3)关闭流:由HDFS的IOUtils工具帮助关闭,不用手动关闭。

(2)文件下载

文件下载与上传正好相反,即从HDFS读取文件,本地写文件。对应I/O流分别为HDFS(输入流),本地(输出流)

文件下载步骤如下:

1)创建HDFS输入流:FSDataInputStream fin = fs.open(new Path("/hdfs_test/xxx.txt"))

2)创建本地输出流:  FileOutputStream fout = new FileOutputStream(new File("C:\\xxx.txt"));

3)关闭流:由HDFS的IOUtils工具帮助关闭,不用手动关闭。

文件上传、下载测试代码如下:

package com.ethan.hdfs;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;

/**
 * I/O流操作HDFS
 * 完成文件上传和下载
 * @author Ethan
 *
 */
public class HdfsIOTest {

	public static void main(String[] args) {

		Configuration conf = new Configuration();
		try {
			FileSystem fs = FileSystem.get(new URI("hdfs://xxx.xxx.xx.xxx:9000"), conf, "root");
			
			// 上传 (本地(输入流)-->HDFS(输出流))从本地读取,写到HDFS
			// 本地的输入流:普通输入流
			// HDFS输出流:HDFS专有的
			// 1.创建输入流
			FileInputStream in = new FileInputStream(new File("C:\\xxx.txt"));
			// 2.创建HDFS输出流
			// 流的方式必须指定一个文件名,这与Api方式不一样了
			FSDataOutputStream out = fs.create(new Path("/hdfs_test/xxxx.txt"));
			IOUtils.copyBytes(in, out, 4396);
			// IOUtils帮助关闭流  --> 这与fs操作不同
			// in.close(); out.close();
			// 文件下载
			// 从HDFS(读 输入流)-->本地(写 输出流)
			// 创建HDFS输入流
			FSDataInputStream fin = fs.open(new Path("/hdfs_test/a"));
			// 创建本地输出流
			FileOutputStream fout = new FileOutputStream(new File("D:\\xxx.txt"));
			IOUtils.copyBytes(fin, fout, 4396);
			
		} catch (IOException | InterruptedException | URISyntaxException e) {
			e.printStackTrace();
		}
	}
}

综上,就是通过指令、API和I/O操作HDFS的常用方法。

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值