接上节创建hadoop.txt
能否事先判断文件是否存在呢?我们去查看Hadoop FileSystem API文档
https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/filesystem/filesystem.html
查看exists(Path path)
方法
编写create2()
方法,事先判断文件是否存在
@Test
public void create2() throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 定义统一资源标识符(uri:uniform resource identifier)
String uri = "hdfs://master:9000";
// 创建文件系统对象(基于HDFS的文件系统)
FileSystem fs = FileSystem.get(new URI(uri), conf);
// 创建路径对象
Path path = new Path(uri + "/ied01/hadoop.txt");
// 判断路径对象指向的文件是否存在
if (fs.exists(path)) {
// 提示用户文件已存在
System.out.println("文件[" + path + "]已经存在!");
} else {
// 基于路径对象创建文件
boolean result = fs.createNewFile(path);
// 根据返回值判断文件是否创建成功
if (result) {
System.out.println("文件[" + path + "]创建成功!");
} else {
System.out.println("文件[" + path + "]创建失败!");
}
}
}
运行程序
故意让HDFS进入安全模式(只能读,不能写),看看会出现什么状况?
删除已经创建的/ied01/hadoop.txt
执行命令:hdfs dfsadmin -safemode enter
此时,再运行程序,查看结果,抛出SafeModeException
异常
下面,修改程序,来处理这个可能会抛出的安全模式异常
@Test
public void create2() throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 定义统一资源标识符(uri: uniform resource identifier)
String uri = "hdfs://master:9000";
// 创建文件系统对象(基于HDFS的文件系统)
FileSystem fs = FileSystem.get(new URI(uri), conf);
// 创建路径对象(指向文件)
Path path = new Path(uri + "/ied01/hadoop.txt");
// 判断路径对象指向的文件是否存在
if (fs.exists(path)) {
// 提示用户文件已存在
System.out.println("文件[" + path + "]已经存在!");
} else {
try {
// 基于路径对象创建文件
boolean result = fs.createNewFile(path);
// 根据返回值判断文件是否创建成功
if (result) {
System.out.println("文件[" + path + "]创建成功!");
} else {
System.out.println("文件[" + path + "]创建失败!");
}
} catch (Exception e) {
System.err.println("异常信息:" + e.getMessage());
}
}
}
运行程序,查看结果(虽然我们捕获了安全模式异常,但还是有一点系统抛出的异常信息)
关闭安全模式,执行命令:hdfs dfsadmin -safemode leave
再运行程序,查看效果
写入HDFS文件
类似于HDFS Shell里的hdfs dfs -put
命令
在net.hw.hdfs
包里创建WriteFileOnHDFS
类
将数据直接写入HDFS文件
任务:在/ied01
目录里创建hello.txt
文件
创建write1()方法
运行write1()
测试方法,查看结果,抛出RemoteException
异常,三个数据节点都在运行,但是无法写入数据
修改代码,设置数据节点主机名属性,如下图所示
运行程序,查看结果 利用Hadoop WebUI查看hello.txt
文件
将本地文件写入HDFS文件
在项目根目录创建一个文本文件test.txt
创建write2()
方法
@Test
public void write2() throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 定义统一资源标识符(uri: uniform resource identifier)
String uri = "hdfs://master:9000";
// 创建文件系统对象(基于HDFS的文件系统)
FileSystem fs = FileSystem.get(new URI(uri), conf, "root");
// 创建路径对象(指向文件)
Path path = new Path(uri + "/ied01/exam.txt");
// 创建文件系统数据字节输出流(出水管:数据从程序到文件)
FSDataOutputStream out = fs.create(path);
// 创建文件字符输入流对象(进水管:数据从文件到程序)
FileReader fr = new FileReader("test.txt");
// 创建缓冲字符输入流对象
BufferedReader br = new BufferedReader(fr);
// 定义行字符串变量
String nextLine = "";
// 通过循环遍历缓冲字符输入流
while ((nextLine = br.readLine()) != null) {
// 在控制台输出读取的行
System.out.println(nextLine);
// 通过文件系统数据字节输出流对象写入指定文件
out.write((nextLine + "\n").getBytes());
}
// 关闭缓冲字符输入流
br.close();
// 关闭文件字符输入流
fr.close();
// 关闭文件系统数据字节输出流
out.close();
// 提示用户写入文件成功
System.out.println("本地文件[test.txt]成功写入[" + path + "]!");
}
运行write2()
测试方法,查看结果
查看/ied01/exam.txt
内容
其实这个方法的功能就是将本地文件复制(上传)到HDFS,有没有更简单的处理方法呢?有的,通过使用一个工具类IOUtils
来完成文件的相关操作。
编写write2_()
方法
@Test
public void write2_() throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 定义统一资源标识符(uri: uniform resource identifier)
String uri = "hdfs://master:9000";
// 创建文件系统对象(基于HDFS的文件系统)
FileSystem fs = FileSystem.get(new URI(uri), conf, "root");
// 创建路径对象(指向文件)
Path path = new Path(uri + "/ied01/test.txt");
// 创建文件系统数据字节输出流(出水管:数据从程序到文件)
FSDataOutputStream out = fs.create(path);
// 创建文件字节输入流(进水管:数据从文件到程序)
FileInputStream in = new FileInputStream("test.txt");
// 利用IOUtils类提供的字节拷贝方法在控制台显示文件内容
IOUtils.copyBytes(in, System.out, 1024, false);
// 利用IOUtils类提供的字节拷贝方法来复制文件
IOUtils.copyBytes(in, out, conf);
// 关闭文件字节输入流
in.close();
// 关闭文件系统数据字节输出流
out.close();
// 提示用户写入文件成功
System.out.println("本地文件[test.txt]成功写入[" + path + "]!");
}
运行write2_()
测试方法,查看结果
查看/ied01/test.txt
内容,文件是存在的,但是没有内容
因为字节输入流的数据已经输出到到控制台,此时字节输入流里已经没有数据,此时执行IOUtils.copyBytes(in, out, conf);
,因此输出流肯定也没有数据可以写入文件,那该怎么办呢?再次读取文件,让字节输入流有数据。
运行write2_()
方法,查看结果
查看/ied01/test.txt
文件
读取HDFS文件
相当于Shell里的两个命令:hdfs dfs -cat
和hdfs dfs -get
在net.hw.hdfs
包里创建ReadFileOnHDFS
类
读取HDFS文件直接在控制台显示
准备读取hdfs://master:9000/ied01/test.txt
文件
编写read1()
方法
运行read1()
测试方法,查看结果
其实,我们可以使用IOUtils
类来简化代码,创建read1_()
测试方法
再运行read1_()
测试方法,查看结果
读取HDFS文件,保存为本地文件
任务:将/ied01/test.txt
下载到项目的download
目录里
创建download
目录
创建read2()
方法
@Test
public void read2() throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 定义统一资源标识符(uri: uniform resource identifier)
String uri = "hdfs://master:9000";
// 创建文件系统对象(基于HDFS的文件系统)
FileSystem fs = FileSystem.get(new URI(uri), conf, "root");
// 创建路径对象(指向文件)
Path path = new Path(uri + "/ied01/test.txt");
// 创建文件系统数据字节输入流(进水管:数据从文件到程序)
FSDataInputStream in = fs.open(path);
// 创建文件字节输出流(出水管:数据从程序到文件)
FileOutputStream out = new FileOutputStream("download/exam.txt");
// 利用IOUtils工具类读取HDFS文件(靠输入流),写入本地文件(靠输出流)
IOUtils.copyBytes(in, out, conf);
// 关闭文件字节输出流
out.close();
// 关闭文件系统数据字节流输入流
in.close();
// 关闭文件系统
fs.close();
// 提示用户文件下载成功
System.out.println("文件[" + path + "]下载到本地文件[download/exam.txt]!");
}
运行read2()
测试方法,查看结果
重命名目录或文件
相当于Shell里的hdfs dfs -mv
命令
在net.hw.hdfs
包里创建RenameDirOrFile
类
重命名目录
任务:将/ied01
目录更名为/lzy01
编写renameDir()
方法
运行renameDir()
方法,查看结果
利用Hadoop WebUI界面查看
重命名文件
任务:将lzy01
目录下的hello.txt
重命名为hi.txt
编写renameFile()
方法
@Test
public void renameFile() throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 定义统一资源标识符(uri: uniform resource identifier)
String uri = "hdfs://master:9000";
// 创建文件系统对象(基于HDFS的文件系统)
FileSystem fs = FileSystem.get(new URI(uri), conf, "root");
// 创建源路径对象(指向文件)
Path sourcePath = new Path(uri + "/lzy01/hello.txt");
// 创建目标路径对象(指向文件)
Path destinationPath = new Path(uri + "/lzy01/hi.txt");
// 利用文件系统对象重命名文件
fs.rename(sourcePath, destinationPath);
// 关闭文件系统
fs.close();
// 提示用户文件更名成功
System.out.println("文件[" + sourcePath.getName() + "]更名为文件[" + destinationPath.getName() + "]!");
}
运行renameFile()
测试方法,查看结果
利用Hadoop WebUI界面查看
显示文件列表
在net.hw.hdfs
包里创建ListHDFSFiles
类
显示指定目录下文件全部信息
任务:显示/lzy01
目录下的文件列表
编写list1()
方法
文件系统对象的listFiles()
方法第二个参数设置true
,这样可以将/lzy01
目录的一切文件,包括子目录里的文件,都一网打尽
运行list1()
测试方法,查看结果
上述文件状态对象封装的有关信息,可以通过相应的方法来获取,比如getPath()
方法就可以获取路径信息,getLen()
方法就可以获取文件长度信息……
显示指定目录下文件路径和长度信息
编写list2()
方法
@Test
public void list2() throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 定义uri字符串
String uri = "hdfs://master:9000";
// 创建文件系统对象
FileSystem fs = FileSystem.get(new URI(uri), conf, "root");
// 创建远程迭代器对象,泛型是位置文件状态类(相当于`hdfs dfs -ls -R /lzy01`)
RemoteIterator<LocatedFileStatus> ri = fs.listFiles(new Path("/lzy01"), true);
// 遍历远程迭代器
while (ri.hasNext()) {
LocatedFileStatus lfs = ri.next();
System.out.println(lfs.getPath() + " " + lfs.getLen() + "字节");
}
}
运行list2()
测试方法,查看结果
对照Hadoop WebUI上给出的文件长度信息
获取文件块信息
任务:获取/lzy01/hadoop-3.3.4.tar.gz
文件块信息
hadoop压缩包会分割成6个文件块
创建GetBlockLocations
类
用到getFileBlockLocations()
方法来获取物理切块信息
编写代码,获取文件块信息
运行程序,查看结果(切点位置,块大小,块存在位置
由此可见,hadoop-3.3.4.tar.gz
被hadoop物理切分成6
块,前5
块长度均为134217728
字节(128
MB),第6
块长度为24369142
字节(23.24
MB)。
创建目录
任务:在HDFS上创建/ied01
目录
在net.hw.hdfs
包里创建MakeDirOnHDFS
类
package net.hw.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 MakeDirOnHDFS {
public static void main(String[] args) throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 定义uri字符串
String uri = "hdfs://master:9000";
// 创建文件系统对象
FileSystem fs = FileSystem.get(new URI(uri), conf, "root");
// 创建路径对象,指向目录
Path path = new Path("/ied01");
// 利用文件系统创建指定目录
boolean result = fs.mkdirs(path);
// 判断目录是否创建成功
if (result) {
System.out.println("目录[" + path + "]创建成功!" );
} else {
System.out.println("目录[" + path + "]创建失败!" );
}
}
}
运行程序,查看结果
利用Hadoop WebUI界面查看
判断目录或文件是否存在
任务:判断HDFS上/ied01
目录是否存在,判断/ied01/hadoop.txt
文件是否存在
在net.hw.hdfs
包里创建DirFileExistsOrNot
类
编写dirExists()
方法
package net.hw.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.junit.Test;
import java.net.URI;
public class DirFileExistsOrNot {
@Test
public void dirExists() throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 定义uri字符串
String uri = "hdfs://master:9000";
// 创建文件系统对象
FileSystem fs = FileSystem.get(new URI(uri), conf, "root");
// 创建路径对象,指向目录
Path path = new Path("/ied01");
// 判断目录是否存在
if (fs.exists(path)) {
System.out.println("目录[" + path + "]存在!");
} else {
System.out.println("目录[" + path + "]不存在!");
}
}
}
运行程序,查看结果
修改代码,再测试
编写fileExists()
方法
@Test
public void fileExists() throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 定义uri字符串
String uri = "hdfs://master:9000";
// 创建文件系统对象
FileSystem fs = FileSystem.get(new URI(uri), conf, "root");
// 创建路径对象,指向文件
Path path = new Path("/ied01/music.txt");
// 判断文件是否存在
if (fs.exists(path)) {
System.out.println("文件[" + path + "]存在!");
} else {
System.out.println("文件[" + path + "]不存在!");
}
}
运行程序,查看结果
判断Path指向目录还是文件
在net.hw.hdfs
包里创建PathToFileOrDir
类
package net.hw.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 PathToFileOrDir {
public static void main(String[] args) throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 定义uri字符串
String uri = "hdfs://master:9000";
// 创建文件系统对象
FileSystem fs = FileSystem.get(new URI(uri), conf, "root");
// 创建路径对象,指向目录
Path path1 = new Path("/ied08");
if (fs.isDirectory(path1)) {
System.out.println("[" + path1 + "]指向的是目录!");
} else {
System.out.println("[" + path1 + "]指向的是文件!");
}
// 创建路径对象,指向文件
Path path2 = new Path("/lzy01/howard.txt");
if (fs.isFile(path2)) {
System.out.println("[" + path2 + "]指向的是文件!");
} else {
System.out.println("[" + path2 + "]指向的是目录!");
}
}
}
运行程序,查看结果
结果明显不对,说明程序逻辑上有问题,原因在于/ied08
目录不存在,/lzy01/howard.txt
文件不存在,修改代码,加上目录或文件存在与否的判断
运行程序,查看结果
修改代码,改成存在的目录和文件
package net.hw.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 PathToFileOrDir {
public static void main(String[] args) throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 定义uri字符串
String uri = "hdfs://master:9000";
// 创建文件系统对象
FileSystem fs = FileSystem.get(new URI(uri), conf, "root");
// 创建路径对象,指向目录
Path path1 = new Path("/ied01");
if (fs.exists(path1)) {
if (fs.isDirectory(path1)) {
System.out.println("[" + path1 + "]指向的是目录!");
} else {
System.out.println("[" + path1 + "]指向的是文件!");
}
} else {
System.out.println("路径[" + path1 + "]不存在!");
}
// 创建路径对象,指向文件
Path path2 = new Path("/lzy01/test.txt");
if (fs.exists(path2)) {
if (fs.isFile(path2)) {
System.out.println("[" + path2 + "]指向的是文件!");
} else {
System.out.println("[" + path2 + "]指向的是目录!");
}
} else {
System.out.println("路径[" + path2 + "]不存在!");
}
}
}
运行程序,查看结果
删除目录或文件
类似于HDFS Shell里的hdfs dfs -rmdir
和hdfs dfs -rm -r
命令
在net.hw.hdfs
包里创建DeleteFileOrDir
类
删除文件
任务:删除/lzy01/hi.txt
文件
编写deleteFile()
方法
package net.hw.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.junit.Test;
import java.net.URI;
public class DeleteFileOrDir {
@Test
public void deleteFile() throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 定义uri字符串
String uri = "hdfs://master:9000";
// 创建文件系统对象
FileSystem fs = FileSystem.get(new URI(uri), conf, "root");
// 创建路径对象(指向文件)
Path path = new Path(uri + "/lzy01/hi.txt");
// 删除路径对象指向的文件(第二个参数表明是否递归,删除文件,不用递归)
boolean result = fs.delete(path, false);
// 根据返回结果提示用户
if (result) {
System.out.println("文件[" + path + "]删除成功!");
} else {
System.out.println("文件[" + path + "]删除失败!");
}
}
}
运行deleteFile()
测试方法,查看结果
再运行deleteFile()
测试方法,查看结果
可以在删除文件之前,判断文件是否存在,需要修改代码
@Test
public void deleteFile() throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 定义uri字符串
String uri = "hdfs://master:9000";
// 创建文件系统对象
FileSystem fs = FileSystem.get(new URI(uri), conf, "root");
// 创建路径对象(指向文件)
Path path = new Path(uri + "/lzy01/hi.txt");
// 判断路径对象指向的文件是否存在
if (fs.exists(path)) {
// 删除路径对象指向的文件(第二个参数表明是否递归,删除文件,不用递归)
boolean result = fs.delete(path, false);
// 根据返回结果提示用户
if (result) {
System.out.println("文件[" + path + "]删除成功!");
} else {
System.out.println("文件[" + path + "]删除失败!");
}
} else {
System.out.println("文件[" + path + "]不存在!");
}
}
此时运行deleteFile()
测试方法,查看结果
删除目录
任务:删除/lzy01
目录
编写deleteDir()
方法
@Test
public void deleteDir() throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 定义uri字符串
String uri = "hdfs://master:9000";
// 创建文件系统对象
FileSystem fs = FileSystem.get(new URI(uri), conf, "root");
// 创建路径对象(指向目录)
Path path = new Path(uri + "/lzy01");
// 判断路径对象指向的目录否存在
if (fs.exists(path)) {
// 删除路径对象指向的目录(第二个参数表明是否递归,删除文件,要递归)
boolean result = fs.delete(path, true);
// 根据返回结果提示用户
if (result) {
System.out.println("目录[" + path + "]删除成功!");
} else {
System.out.println("目录[" + path + "]删除失败!");
}
} else {
System.out.println("目录[" + path + "]不存在!");
}
}
运行deleteDir()
方法,查看结果
再运行deleteDir()
方法,查看结果
删除目录或文件
进行三个层面的判断:判断是否存在、判断类型(目录或文件)、判断删除是否成功
任务:删除/ied02/exam.txt
文件和/lzy01
目录
编写delete()
方法
@Test
public void delete() throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 定义uri字符串
String uri = "hdfs://master:9000";
// 创建文件系统对象
FileSystem fs = FileSystem.get(new URI(uri), conf, "root");
// 定义随机对象
Random random = new Random();
// 产生随机整数 - [0, 1]
int choice = random.nextInt(100) % 2;
// 定义路径字符串
String[] strPath = {"/ied02/exam.txt", "/lzy01"};
// 创建路径对象(指向目录或文件)
Path path = new Path(uri + strPath[choice]);
// 判断存在性
if (fs.exists(path)) {
// 判断类型:目录或文件
String type = "";
if (fs.isDirectory(path)) {
type = "目录";
} else {
type = "文件";
}
// 删除路径对象指向的目录或文件
boolean result = fs.delete(path, true);
// 判断删除是否成功
if (result) {
System.out.println(type + "[" + path + "]删除成功!");
} else {
System.out.println(type + "[" + path + "]删除失败!");
}
} else {
System.out.println("路径[" + path + "]不存在!");
}
}
运行程序,查看结果
再次运行程序,查看结果