《大数据技术原理与应用》实验报告二 熟悉常用的HDFS操作
目 录
一、实验目的
二、实验环境
三、实验内容与完成情况
1. 利用Hadoop提供的Shell命令完成下列任务
2. 编程实现类‘MyFSDataInputStream’
3. 编程实现指定文件的文本到终端中
四、问题和解决方法
五、心得体会
一、实验目的
1. 理解HDFS在Hadoop体系结构中的角色。
2. 熟练使用HDFS操作常用的Shell命令。
3. 熟悉HDFS操作常用的Java API。
二、实验环境
1. Oracle VM VirtualBox虚拟机
2. 系统版本Ubuntu 18.04 64
3. JDK1.8版本
4. Hadoop3.1.3
5. Windows11
6. Java IDE:Eclipse
三、实验内容与完成情况
1. 利用Hadoop提供的Shell命令完成下列任务
(1)向HDFS中上传任意文本文件,如果指定的文件在HDFS中已经存在,由用户指定是追加到原有文件末尾还是覆盖原有的文件。
① 使用以下Shell命令查看text.txt文件是否存在:
cd /usr/local/hadoop
./bin/hdfs dfs -test -e text.txt
② 使用以下Shell命令进行上一步执行结果的查看(显示0则表示文件存在,显示1表示文件不存在):
echo $?
③ 使用以下Shell命令将文件local.txt的内容追加到文件text.txt的末尾并查看追加后的文件内容:
./bin/hdfs dfs -appendToFile /home/hadoop/local.txt text.txt./bin/hdfs dfs -cat /user/hadoop/text.txt
④ 使用以下Shell命令将原有文件text.txt进行覆盖并查看覆盖后的文件内容:
./bin/hdfs dfs -copyFromLocal -f /home/hadoop/local.txt text.txt./bin/hdfs dfs -cat /user/hadoop/text.txt
⑤ 利用Eclipse软件建立HDFSApi类对原文件进行追加和覆盖等操作:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import java.io.*;public class HDFSApi {// 判断路径是否存在public static boolean test(Configuration conf, String path) throws IOException {FileSystem fs = FileSystem.get(conf);return fs.exists(new Path(path));}/*** 复制文件到指定路径* 若路径已存在,则进行覆盖*/public static void copyFromLocalFile(Configuration conf, String localFilePath, String remoteFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path localPath = new Path(localFilePath);Path remotePath = new Path(remoteFilePath);// fs.copyFromLocalFile 第一个参数表示是否删除源文件,第二个参数表示是否覆盖fs.copyFromLocalFile(false, true, localPath, remotePath);fs.close();}/*** 追加文件内容*/public static void appendToFile(Configuration conf, String localFilePath, String remoteFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);// 创建一个文件读入流FileInputStream in = new FileInputStream(localFilePath);// 创建一个文件输出流,输出的内容将追加到文件末尾FSDataOutputStream out = fs.append(remotePath);// 读写文件内容byte[] data = new byte[1024];int read;while ((read = in.read(data)) > 0) {out.write(data, 0, read);}out.close();in.close();fs.close();}/*** 主函数*/public static void main(String[] args) {Configuration conf = new Configuration();conf.set("fs.default.name", "hdfs://localhost:9000"); // 配置HDFS地址String localFilePath = "/home/hadoop/text.txt"; // 本地路径String remoteFilePath = "/user/hadoop/text.txt"; // HDFS路径String choice = "append"; // 若文件存在则追加到文件末尾// String choice = "overwrite"; // 若文件存在则覆盖try {// 判断文件是否存在boolean fileExists = HDFSApi.test(conf, remoteFilePath);if (fileExists) {System.out.println(remoteFilePath + " 已存在.");} else {System.out.println(remoteFilePath + " 不存在.");}// 根据不同情况进行处理if (!fileExists) { // 文件不存在,则上传HDFSApi.copyFromLocalFile(conf, localFilePath, remoteFilePath);System.out.println(localFilePath + " 已上传至 " + remoteFilePath);} else if ("overwrite".equals(choice)) { // 选择覆盖HDFSApi.copyFromLocalFile(conf, localFilePath, remoteFilePath);System.out.println(localFilePath + " 已覆盖 " + remoteFilePath);} else if ("append".equals(choice)) { // 选择追加HDFSApi.appendToFile(conf, localFilePath, remoteFilePath);System.out.println(localFilePath + " 已追加至 " + remoteFilePath);}} catch (Exception e) {e.printStackTrace();}}
}
(2)从HDFS中下载指定文件,如果本地文件与要下载的文件名称相同,则自动对下载的文件重命名。
①使用以下Shell命令进行文件的下载,当text.txt文件名称与本地的文件名称相同时则将其命名为text2.txt,否则直接进行下载:
if $(./bin/hdfs dfs -test -e file:///home/hadoop/text.txt);then $(./bin/hdfs dfs -copyToLocal text.txt ./text2.txt);then $(./bin/hdfs dfs -copyToLocal text.txt ./text.txt);fi
②文件下载成功后使用以下Shell命令查看文件是否存在:
ls
③利用Eclipse软件建立HDFSApi类实现指定文件的下载:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import java.io.*;public class HDFSApi {/*** 下载文件到本地* 判断本地路径是否已存在,若已存在,则自动进行重命名*/public static void copyToLocal(Configuration conf, String remoteFilePath, String localFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);File f = new File(localFilePath);// 如果文件名存在,自动重命名(在文件名后面加上 _0, _1 ...)if (f.exists()) {System.out.println(localFilePath + " 已存在.");Integer i = 0;while (true) {f = new File(localFilePath + "_" + i.toString());if (!f.exists()) {localFilePath = localFilePath + "_" + i.toString();break;}i++;}System.out.println("将重新命名为: " + localFilePath);}// 下载文件到本地Path localPath = new Path(localFilePath);fs.copyToLocalFile(remotePath, localPath);fs.close();}/*** 主函数*/public static void main(String[] args) {Configuration conf = new Configuration();conf.set("fs.default.name", "hdfs://localhost:9000");String localFilePath = "/home/hadoop/text.txt"; // 本地路径String remoteFilePath = "/user/hadoop/text.txt"; // HDFS路径try {HDFSApi.copyToLocal(conf, remoteFilePath, localFilePath);System.out.println("下载完成");} catch (Exception e) {e.printStackTrace();}}
}
(3)将HDFS中指定文件的内容输出到终端中。
①使用以下Shell命令将指定文件的内容输出到终端:
./bin/hdfs dfs -cat /user/hadoop/text.txt
②利用Eclipse软件建立HDFSApi类实现指定文件内容的输出:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import java.io.*;public class HDFSApi {/*** 读取文件内容*/public static void cat(Configuration conf, String remoteFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);FSDataInputStream in = fs.open(remotePath);BufferedReader reader = new BufferedReader(new InputStreamReader(in));String line;while ((line = reader.readLine()) != null) {System.out.println(line);}reader.close();in.close();fs.close();}/*** 主函数*/public static void main(String[] args) {Configuration conf = new Configuration();conf.set("fs.default.name", "hdfs://localhost:9000");String remoteFilePath = "/user/hadoop/text.txt"; // HDFS路径try {System.out.println("读取文件: " + remoteFilePath);HDFSApi.cat(conf, remoteFilePath);System.out.println("\n读取完成");} catch (Exception e) {e.printStackTrace();}}
}
(4)显示HDFS中指定的文件的读写权限、大小、创建时间、路径等信息;执行下列命令。
①使用以下Shell命令显示HDFS中指定文件的读写权限、大小、创建时间、路径等信息:
./bin/hdfs dfs -ls -h /user/hadoop/text.txt
②利用Eclipse软件建立HDFSApi类实现指定文件的读写权限、大小、创建时间、路径等信息:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import java.io.*;
import java.text.SimpleDateFormat;public class HDFSApi {/*** 显示指定文件的信息*/public static void ls(Configuration conf, String remoteFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);FileStatus[] fileStatuses = fs.listStatus(remotePath);for (FileStatus s : fileStatuses) {System.out.println("路径: " + s.getPath().toString());System.out.println("权限: " + s.getPermission().toString());System.out.println("大小: " + s.getLen());// 返回的是时间戳,转化为时间日期格式Long timeStamp = s.getModificationTime();SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String date = format.format(timeStamp);System.out.println("时间: " + date);}fs.close();}/*** 主函数*/public static void main(String[] args) {Configuration conf = new Configuration();conf.set("fs.default.name", "hdfs://localhost:9000");String remoteFilePath = "/user/hadoop/text.txt"; // HDFS路径try {System.out.println("读取文件信息: " + remoteFilePath);HDFSApi.ls(conf, remoteFilePath);System.out.println("\n读取完成");} catch (Exception e) {e.printStackTrace();}}
}
(5)给定HDFS中某一个目录,输出该目录下的所有文件的读写权限、大小、创建时间、路径等信息,如果该文件是目录,则递归输出该目录下所有文件相关信息。
①使用以下Shell命令输出该目录下的所有文件的读写权限、大小、创建时间、路径等信息:
./bin/hdfs dfs -ls -R -h /user/hadoop
②利用Eclipse软件建立HDFSApi类实现输出该目录下的所有文件的读写权限、大小、创建时间、路径等信息:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import java.io.*;
import java.text.SimpleDateFormat;public class HDFSApi {/*** 显示指定文件夹下所有文件的信息(递归)*/public static void lsDir(Configuration conf, String remoteDir) throws IOException {FileSystem fs = FileSystem.get(conf);Path dirPath = new Path(remoteDir);// 递归获取目录下的所有文件RemoteIterator<LocatedFileStatus> remoteIterator = fs.listFiles(dirPath, true);// 输出每个文件的信息while (remoteIterator.hasNext()) {FileStatus s = remoteIterator.next();System.out.println("路径: " + s.getPath().toString());System.out.println("权限: " + s.getPermission().toString());System.out.println("大小: " + s.getLen());// 返回的是时间戳,转化为时间日期格式Long timeStamp = s.getModificationTime();SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String date = format.format(timeStamp);System.out.println("时间: " + date);System.out.println(); // 输出空行,增加可读性}fs.close();}/*** 主函数*/public static void main(String[] args) {Configuration conf = new Configuration();conf.set("fs.default.name", "hdfs://localhost:9000");String remoteDir = "/user/hadoop"; // HDFS路径try {System.out.println("(递归)读取目录下所有文件的信息: " + remoteDir);HDFSApi.lsDir(conf, remoteDir);System.out.println("读取完成");} catch (Exception e) {e.printStackTrace();}}
}
(6)提供一个HDFS内的文件的路径,对该文件进行创建和删除操作。如果文件所在目录不存在,则自动创建目录。
①使用以下Shell命令对文件进行创建,如果文件所在目录不存在则自动创建目录:
if $(./bin/hdfs dfs -test -d dir1/dir2);then $(./bin/hdfs dfs -touchz dir1/dir2/filename);else $(./bin/hdfs dfs -mkdir -p dir1/dir2 && ./bin/hdfs dfs -touchzdir1/dir2/filename);fi
②文件创建成功后使用以下Shell命令查看文件是否存在:
./bin/hdfs dfs -ls /user/hadoop
③使用以下Shell命令对文件进行删除操作:
./bin/hdfs dfs -rm dir1/dir2/filename
④文件删除成功后使用以下Shell命令查看文件是否删除:
./bin/hdfs dfs -ls /user/hadoop/dir1/dir2
⑤利用Eclipse软件建立HDFSApi类实现对文件的创建和删除操作:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import java.io.*;public class HDFSApi {/*** 判断路径是否存在*/public static boolean test(Configuration conf, String path) throws IOException {FileSystem fs = FileSystem.get(conf);return fs.exists(new Path(path));}/*** 创建目录*/public static boolean mkdir(Configuration conf, String remoteDir) throws IOException {FileSystem fs = FileSystem.get(conf);Path dirPath = new Path(remoteDir);boolean result = fs.mkdirs(dirPath);fs.close();return result;}/*** 创建文件*/public static void touchz(Configuration conf, String remoteFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);FSDataOutputStream outputStream = fs.create(remotePath);outputStream.close();fs.close();}/*** 删除文件*/public static boolean rm(Configuration conf, String remoteFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);boolean result = fs.delete(remotePath, false);fs.close();return result;}/*** 主函数*/public static void main(String[] args) {Configuration conf = new Configuration();conf.set("fs.default.name", "hdfs://localhost:9000");String remoteFilePath = "/user/hadoop/input/text.txt"; // HDFS文件路径String remoteDir = "/user/hadoop/input"; // HDFS目录路径try {// 判断路径是否存在,存在则删除,否则进行创建if (HDFSApi.test(conf, remoteFilePath)) {// 删除文件HDFSApi.rm(conf, remoteFilePath);System.out.println("删除路径: " + remoteFilePath);} else {// 判断目录是否存在,若目录不存在则创建if (!HDFSApi.test(conf, remoteDir)) {HDFSApi.mkdir(conf, remoteDir);System.out.println("创建文件夹: " + remoteDir);}// 创建文件HDFSApi.touchz(conf, remoteFilePath);System.out.println("创建路径: " + remoteFilePath);}} catch (Exception e) {e.printStackTrace();}}
}
(7)提供一个HDFS的目录的路径,对该目录进行创建和删除操作。创建目录时,如果目录文件所在目录不存在则自动创建相应目录;删除目录时,由用户指定当该目录不为空时是否还删除该目录。
①使用以下Shell命令进行目录的创建,如果目录文件所在目录不存在则自动创建相应目录:
./bin/hdfs dfs -mkdir -p dir1/dir2
②使用以下Shell命令进行目录的删除,由用户指定当该目录不为空时是否还删除该目录:
./bin/hdfs dfs -rmdir dir1/dir2
③利用Eclipse软件建立HDFSApi类实现目录的创建和删除:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import java.io.*;public class HDFSApi {/*** 判断路径是否存在*/public static boolean test(Configuration conf, String path) throws IOException {FileSystem fs = FileSystem.get(conf);return fs.exists(new Path(path));}/*** 判断目录是否为空* true: 空,false: 非空*/public static boolean isDirEmpty(Configuration conf, String remoteDir) throws IOException {FileSystem fs = FileSystem.get(conf);Path dirPath = new Path(remoteDir);RemoteIterator<LocatedFileStatus> remoteIterator = fs.listFiles(dirPath, true);return !remoteIterator.hasNext();}/*** 创建目录*/public static boolean mkdir(Configuration conf, String remoteDir) throws IOException {FileSystem fs = FileSystem.get(conf);Path dirPath = new Path(remoteDir);boolean result = fs.mkdirs(dirPath);fs.close();return result;}/*** 删除目录*/public static boolean rmDir(Configuration conf, String remoteDir) throws IOException {FileSystem fs = FileSystem.get(conf);Path dirPath = new Path(remoteDir);// 第二个参数表示是否递归删除所有文件boolean result = fs.delete(dirPath, true);fs.close();return result;}/*** 向文件末尾追加内容*/public static void appendToFileEnd(Configuration conf, String remoteFilePath, String content) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);FSDataOutputStream outputStream = fs.append(remotePath);outputStream.write(content.getBytes());outputStream.close();fs.close();}/*** 向文件开头追加内容*/public static void appendToFileStart(Configuration conf, String remoteFilePath, String content) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);// 创建临时文件用于存放追加的内容Path tempPath = new Path(remoteFilePath + "_temp");FSDataInputStream inputStream = fs.open(remotePath);FSDataOutputStream tempOutputStream = fs.create(tempPath);// 先写入新的内容(即要加到文件开头的内容)tempOutputStream.write(content.getBytes());// 将原文件的内容写入临时文件byte[] buffer = new byte[1024];int bytesRead;while ((bytesRead = inputStream.read(buffer)) > 0) {tempOutputStream.write(buffer, 0, bytesRead);}inputStream.close();tempOutputStream.close();// 删除原文件fs.delete(remotePath, false);// 将临时文件重命名为原文件名fs.rename(tempPath, remotePath);fs.close();}/*** 主函数*/public static void main(String[] args) {Configuration conf = new Configuration();conf.set("fs.default.name", "hdfs://localhost:9000");String remoteDir = "/user/hadoop/input"; // HDFS目录路径String remoteFilePath = "/user/hadoop/input/text.txt"; // HDFS文件路径String contentToAppend = "新追加的内容\n"; // 追加的内容Boolean forceDelete = false; // 是否强制删除try {// 判断目录是否存在,不存在则创建,存在则删除if (!HDFSApi.test(conf, remoteDir)) {HDFSApi.mkdir(conf, remoteDir); // 创建目录System.out.println("创建目录: " + remoteDir);} else {if (HDFSApi.isDirEmpty(conf, remoteDir) || forceDelete) {// 目录为空或强制删除HDFSApi.rmDir(conf, remoteDir);System.out.println("删除目录: " + remoteDir);} else { // 目录不为空System.out.println("目录不为空,不删除: " + remoteDir);}}// 向文件末尾追加内容HDFSApi.appendToFileEnd(conf, remoteFilePath, contentToAppend);System.out.println("内容已追加到文件末尾: " + remoteFilePath);// 向文件开头追加内容HDFSApi.appendToFileStart(conf, remoteFilePath, contentToAppend);System.out.println("内容已追加到文件开头: " + remoteFilePath);} catch (Exception e) {e.printStackTrace();}}
}
(8)向HDFS中指定的文件追加内容,由用户指定将内容追加到原有文件的开头或结尾。
①使用以下Shell命令将内容追加到文件的末尾:
./bin/hdfs dfs -appendToFile local.txt text.txt
②使用以下Shell命令将内容追加到文件的开头:
./bin/hdfs dfs -get text.txtcat text.txt >> local.txt./bin/hdfs dfs -copyFromLocal -f text.txt text.txt
③利用Eclipse软件建立HDFSApi类实现内容在文件开头或结尾的追加:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import java.io.*;public class HDFSApi {/*** 判断路径是否存在*/public static boolean test(Configuration conf, String path) throws IOException {FileSystem fs = FileSystem.get(conf);return fs.exists(new Path(path));}/*** 追加文本内容到文件*/public static void appendContentToFile(Configuration conf, String content, String remoteFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);FSDataOutputStream out = fs.append(remotePath);out.write(content.getBytes());out.close();fs.close();}/*** 追加文件内容(从本地文件)*/public static void appendToFile(Configuration conf, String localFilePath, String remoteFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);FileInputStream in = new FileInputStream(localFilePath);FSDataOutputStream out = fs.append(remotePath);byte[] data = new byte[1024];int read;while ((read = in.read(data)) > 0) {out.write(data, 0, read);}out.close();in.close();fs.close();}/*** 移动文件到本地,移动后删除源文件*/public static void moveToLocalFile(Configuration conf, String remoteFilePath, String localFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);Path localPath = new Path(localFilePath);fs.moveToLocalFile(remotePath, localPath);}/*** 创建文件*/public static void touchz(Configuration conf, String remoteFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);FSDataOutputStream outputStream = fs.create(remotePath);outputStream.close();fs.close();}/*** 删除文件*/public static boolean rm(Configuration conf, String remoteFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);boolean result = fs.delete(remotePath, false);fs.close();return result;}/*** 主函数*/public static void main(String[] args) {Configuration conf = new Configuration();conf.set("fs.default.name", "hdfs://localhost:9000");String remoteFilePath = "/user/hadoop/text.txt"; // HDFS文件路径String content = "新追加的内容\n";String choice = "after"; // 追加到文件末尾// String choice = "before"; // 追加到文件开头try {// 判断文件是否存在if (!HDFSApi.test(conf, remoteFilePath)) {System.out.println("文件不存在: " + remoteFilePath);} else {if (choice.equals("after")) { // 追加在文件末尾HDFSApi.appendContentToFile(conf, content, remoteFilePath);System.out.println("已追加内容到文件末尾: " + remoteFilePath);} else if (choice.equals("before")) { // 追加到文件开头// 没有相应的api可以直接操作,因此先把文件移动到本地// 创建一个新的HDFS,再按顺序追加内容String localTmpPath = "/user/hadoop/tmp.txt";// 移动到本地HDFSApi.moveToLocalFile(conf, remoteFilePath, localTmpPath);// 创建一个新文件HDFSApi.touchz(conf, remoteFilePath);// 先写入新内容HDFSApi.appendContentToFile(conf, content, remoteFilePath);// 再写入原来内容HDFSApi.appendToFile(conf, localTmpPath, remoteFilePath);System.out.println("已追加内容到文件开头: " + remoteFilePath);}}} catch (Exception e) {e.printStackTrace();}// 删除文件操作(演示删除)String deleteFilePath = "/user/hadoop/delete_this_file.txt"; // HDFS文件路径try {if (HDFSApi.test(conf, deleteFilePath)) {boolean result = HDFSApi.rm(conf, deleteFilePath);if (result) {System.out.println("文件删除成功: " + deleteFilePath);} else {System.out.println("删除文件失败: " + deleteFilePath);}}} catch (IOException e) {e.printStackTrace();}}
}
(9)删除HDFS中指定的文件。
①使用以下Shell命令对指定文件进行删除操作:
./bin/hdfs dfs -rm text.txt
②利用Eclipse软件建立HDFSApi类实现指定文件的删除操作:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import java.io.*;public class HDFSApi {/*** 删除文件*/public static boolean rm(Configuration conf, String remoteFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);boolean result = fs.delete(remotePath, false); // 不递归删除子目录和文件fs.close();return result;}/*** 将文件从源路径移动到目标路径*/public static boolean moveFile(Configuration conf, String sourcePath, String destPath) throws IOException {FileSystem fs = FileSystem.get(conf);Path srcPath = new Path(sourcePath);Path destPathObj = new Path(destPath);// 移动文件,如果目标路径存在同名文件,则会覆盖boolean result = fs.rename(srcPath, destPathObj);fs.close();return result;}/*** 主函数*/public static void main(String[] args) {Configuration conf = new Configuration();conf.set("fs.default.name", "hdfs://localhost:9000");String remoteFilePath = "/user/hadoop/text.txt"; // HDFS文件String remoteDestPath = "/user/hadoop/moved_text.txt"; // 目标路径try {// 删除文件操作if (HDFSApi.rm(conf, remoteFilePath)) {System.out.println("文件删除: " + remoteFilePath);} else {System.out.println("操作失败(文件不存在或删除失败)");}// 移动文件操作if (HDFSApi.moveFile(conf, remoteFilePath, remoteDestPath)) {System.out.println("文件已成功移动: " + remoteFilePath + " 到 " + remoteDestPath);} else {System.out.println("文件移动失败");}} catch (Exception e) {e.printStackTrace();}}
}
(10)在HDFS中,将文件从源路径移动到目的路径。
①使用以下Shell命令将文件从源路径移动到目的路径:
./bin/hdfs dfs -mv text.txt text2.txt
②利用Eclipse软件建立HDFSApi类实现将文件从源路径移动到目的路径:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import java.io.*;
import java.nio.charset.StandardCharsets;public class HDFSApi {/*** 移动文件*/public static boolean mv(Configuration conf, String remoteFilePath, String remoteToFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path srcPath = new Path(remoteFilePath);Path dstPath = new Path(remoteToFilePath);boolean result = fs.rename(srcPath, dstPath); // 重命名/移动文件fs.close();return result;}/*** 自定义FSDataInputStream,支持按行读取*/public static class MyFSDataInputStream extends FSDataInputStream {private BufferedReader reader;public MyFSDataInputStream(InputStream in) {super(in);this.reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));}/*** 按行读取文件内容* 如果文件末尾,则返回空*/public String readLine() throws IOException {return reader.readLine(); // 使用BufferedReader按行读取}}/*** 主函数*/public static void main(String[] args) {Configuration conf = new Configuration();conf.set("fs.default.name", "hdfs://localhost:9000");// HDFS源文件和目标文件路径String remoteFilePath = "hdfs:///user/hadoop/text.txt";String remoteToFilePath = "hdfs:///user/hadoop/new.txt";// 测试移动文件功能try {if (HDFSApi.mv(conf, remoteFilePath, remoteToFilePath)) {System.out.println("将文件 " + remoteFilePath + " 移动到 " + remoteToFilePath);} else {System.out.println("操作失败(源文件不存在或移动失败)");}} catch (Exception e) {e.printStackTrace();}// 测试按行读取HDFS文件try {FileSystem fs = FileSystem.get(conf);Path filePath = new Path(remoteToFilePath); // 读取目标文件FSDataInputStream inputStream = fs.open(filePath);// 使用自定义的 MyFSDataInputStream 进行按行读取MyFSDataInputStream myInputStream = new MyFSDataInputStream(inputStream);String line;while ((line = myInputStream.readLine()) != null) {System.out.println("读取的行: " + line);}inputStream.close();fs.close();} catch (IOException e) {e.printStackTrace();}}
}
2. 编程实现类‘MyFSDataInputStream’
编程实现一个类`MyFSDataInputStream`,该类继承`org.apache.hadoop.fs.FSDataInput Stream`,要求如下:
(1)实现按行读取HDFS中指定文件的方法“readLine()”,如果读到文件末尾,则返回空,否则返回文件一行的文本。
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import java.io.*;public class MyFSDataInputStream extends FSDataInputStream {private static final int CACHE_SIZE = 1024; // 缓存区大小private byte[] cache; // 缓存数据private int cacheOffset; // 缓存当前偏移量private int cacheLength; // 缓存当前有效数据的长度public MyFSDataInputStream(InputStream in) {super(in);this.cache = new byte[CACHE_SIZE]; // 初始化缓存this.cacheOffset = 0;this.cacheLength = 0;}/*** 按行读取* 每次读入一个字符,遇到"\n"结束,返回一行内容*/public String readline(BufferedReader br) throws IOException {char[] data = new char[1024];int read = -1;int off = 0;while ((read = br.read(data, off, 1)) != -1) {if (String.valueOf(data[off]).equals("\n")) {off += 1;break;}off += 1;}if (off > 0) {return String.valueOf(data);} else {return null;}}/*** 读取数据,优先使用缓存*/private int readFromCache(byte[] buffer, int offset, int length) {int available = cacheLength - cacheOffset;if (available <= 0) {return -1;}int bytesToCopy = Math.min(available, length);System.arraycopy(cache, cacheOffset, buffer, offset, bytesToCopy);cacheOffset += bytesToCopy;return bytesToCopy;}/*** 从HDFS读取数据,并缓存*/private int readFromHDFS(byte[] buffer, int offset, int length) throws IOException {int bytesRead = super.read(buffer, offset, length);if (bytesRead > 0) {// 缓存数据System.arraycopy(buffer, offset, cache, 0, bytesRead);cacheOffset = 0;cacheLength = bytesRead;}return bytesRead;}/*** 覆盖原始的read方法,先从缓存读取,如果缓存中没有,再从HDFS读取*/@Overridepublic int read(byte[] buffer, int offset, int length) throws IOException {int bytesRead = readFromCache(buffer, offset, length);if (bytesRead == -1) {// 缓存没有数据,从HDFS读取bytesRead = readFromHDFS(buffer, offset, length);}return bytesRead;}/*** 读取文件内容*/public static void cat(Configuration conf, String remoteFilePath) throws IOException {FileSystem fs = FileSystem.get(conf);Path remotePath = new Path(remoteFilePath);FSDataInputStream in = fs.open(remotePath);BufferedReader br = new BufferedReader(new InputStreamReader(in));String line;while ((line = new MyFSDataInputStream(in).readline(br)) != null) {System.out.println(line);}br.close();in.close();fs.close();}/*** 主函数*/public static void main(String[] args) {Configuration conf = new Configuration();conf.set("fs.default.name", "hdfs://localhost:9000");String remoteFilePath = "/user/hadoop/text.txt"; // HDFS路径try {MyFSDataInputStream.cat(conf, remoteFilePath);} catch (Exception e) {e.printStackTrace();}}
}
(2)实现缓存功能,即利用“MyFSDataInputStream”读取若干字节数据时,首先查找缓存,如果缓存中有所需数据,则直接由缓存提供,否则从HDFS中读取数据。
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;public class MyFSDataInputStream extends FSDataInputStream {public MyFSDataInputStream(InputStream in) {super(in);}/*** 实现按行读取* 每次读入一个字符,遇到"\n"结束,返回一行内容*/public static String readline(BufferedReader br) throws IOException {char[] data = new char[1024];int read = -1;int off = 0;while ((read = br.read(data, off, 1)) != -1) {if (String.valueOf(data[off]).equals("\n")) {off += 1;break;}off += 1;}if (off > 0) {return String.valueOf(data, 0, off); // Return the string read so far} else {return null;}}/*** 使用URL和FsURLStreamHandlerFactory来读取HDFS文件* 输出文件内容到终端*/public static void cat(Configuration conf, String remoteFilePath) throws IOException {// 注册HDFS的URL handlerFsURLStreamHandlerFactory fsURLStreamHandlerFactory = new FsURLStreamHandlerFactory();URL.setURLStreamHandlerFactory(fsURLStreamHandlerFactory);// 构建URL对象URL fileUrl = new URL("hdfs", "localhost", 9000, remoteFilePath);URLConnection connection = fileUrl.openConnection();InputStream inputStream = connection.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));String line;while ((line = MyFSDataInputStream.readline(br)) != null) {System.out.println(line); // 打印文件内容}br.close();inputStream.close();}/*** 主函数*/public static void main(String[] args) {Configuration conf = new Configuration();conf.set("fs.default.name", "hdfs://localhost:9000");String remoteFilePath = "/user/hadoop/text.txt"; // HDFS文件路径try {MyFSDataInputStream.cat(conf, remoteFilePath); // 调用cat方法读取文件} catch (Exception e) {e.printStackTrace();}}
}
3. 编程实现指定文件的文本到终端中
查看Java帮助手册或其它资料,用`java.net.URL`和`org.apache.hadoop.fs.FsURLStream HandlerFactory`编程完成输出HDFS中指定文件的文本到终端中。
import org.apache.hadoop.fs.*;
import org.apache.hadoop.io.IOUtils;
import java.io.*;
import java.net.URL;public class HDFSApi {static {// 设置URL的流处理器,使其能够识别HDFS协议URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());}/*** 主函数:通过URL读取HDFS文件并输出到控制台*/public static void main(String[] args) throws Exception {// HDFS文件路径String remoteFilePath = "hdfs:///user/hadoop/text.txt"; // 初始化输入流InputStream in = null;try {// 通过URL对象打开HDFS文件的数据流in = new URL(remoteFilePath).openStream();// 将HDFS文件的内容复制到控制台输出IOUtils.copyBytes(in, System.out, 4096, false);} finally {// 关闭输入流IOUtils.closeStream(in);}}
}
四、问题和解决方法
1. 实验问题:Hadoop无法启动或启动失败。
解决方法:检查Hadoop配置文件中的路径和参数设置,确保正确设置以及文件的可访问性,对配置文件进行修改后对应问题得到解决。
2. 实验问题:Hadoop的任务无法运行或失败。
解决方法:检查任务配置文件中的输入输出路径是否正确,并确保输入数据存在,更改输入输出路径后问题得到解决。
3. 实验问题:执行命令时提示"命令未找到"。
解决方法:确保命令拼写正确,并检查命令是否安装在系统路径中。可以使用which命令来确定命令的路径,并将其添加到系统路径中。
4. 实验问题:没有足够的权限执行某个命令。
解决方法:尝试使用sudo命令以管理员权限运行命令,或者联系系统管理员赋予所需的权限。
5. 实验问题:Hadoop集群的节点之间无法通信。
解决方法:检查网络设置,确保集群节点之间可以相互访问。还可以尝试使用telnet命令来测试节点之间的连接进而避免错误的出现。
6. 实验问题:怎样在后台运行命令。
解决方法:使用&符号将命令置于后台运行,或者可以使用nohup命令将命令与文件描述符0(标准输入)断开连接,以允许在终端会话结束后继续运行。
7. 实验问题:执行命令时遇到文件或目录不存在的错误。
解决方法:确保输入的文件或目录路径正确,并且对其具有适当的权限。可以使用ls命令来查看文件或目录是否存在。
8. 实验问题:需要查找包含特定文本的文件。
解决方法:可以使用grep命令来在文件中搜索特定文本。例如,grep "keyword" filename将在给定的文件中查找匹配的关键字。
9. 实验问题:Hadoop的性能较低。
解决方法:可以调整Hadoop配置文件中的参数,如调整内存分配、调整并发任务数等,还可以考虑增加集群的节点数以提高处理能力。
10. 实验问题:删除文件或目录时出错,提示权限不足。
解决方法:确保具有删除文件或目录的权限。如果是目录,则使用rm -r命令以递归方式删除整个目录及其内容。
11. 实验问题:了解命令的使用方法和参数。
解决方法:使用man命令(例如:man command)来获取命令的手册页,并了解命令的使用方法和参数。
12. 实验问题:在多个文件之间复制、移动或重命名。
解决方法:使用cp命令进行文件复制,mv命令进行文件移动和重命名。确保给出正确的源文件路径和目标路径。
13. 实验问题:创建一个新的空文件。
解决方法:使用touch命令创建一个新的空文件。例如,touch filename将创建一个名为Filename的空文件。
14. 实验问题:Hadoop的数据丢失或损坏。
解决方法:设置适当的数据备份策略,如使用HDFS的副本功能来备份数据。此外,定期进行数据备份和恢复测试,以确保数据的完整性。
15. 实验问题:忘记了当前工作目录的路径。
解决方法:使用pwd命令查看当前工作目录的路径。
16. 实验问题:打算执行的命令使用了很长的参数。
解决方法:编写一个脚本文件,在脚本中定义命令和参数,并用chmod命令添加可执行权限,然后直接执行脚本。
17. 实验问题:需要查看文件的前几行或后几行。
解决方法:使用head命令查看文件的前几行,使用tail命令查看文件的后几行。
18. 实验问题:需要将命令的输出结果保存到文件中。
解决方法:使用重定向操作符>将命令输出写入文件。
19. 实验问题:查找文件或目录的权限信息。
解决方法:使用ls -l命令可以列出文件和目录的详细信息,包括权限信息、所有者和大小等。
20. 实验问题:压缩或解压缩文件。
解决方法:使用tar命令进行文件压缩和解压缩。例如,使用tar -czvf archive.tar.gz directory可以将一个目录压缩为.tar.gz文件。
21. 实验问题:需要在文件中替换特定文本。
解决方法:使用sed命令进行文本替换。例如,sed 's/a/b/g' filename将文件中所有出现的"a"替换为"b"。
22. 实验问题:快速访问最近使用的命令。
解决方法:使用命令历史和快捷键。按下上箭头键可以在命令历史中向上导航,并按下回车键执行选中的命令。使用Ctrl + R可以进行反向搜索并执行最近使用的命令。
23. 实验问题:无法连接到互联网、网络速度缓慢、Wi-Fi连接时出现问题。
解决方法:检查网络设置,确保已正确配置IP地址和网关。如果使用的是Wi-Fi连接,检查是否已正确输入密码,并尝试重新启动网络管理器服务来重置网络配置。
24. 实验问题:应用程序崩溃或卡死。
解决方法:由于库依赖错误、磁盘空间不足、损坏的配置文件导致的。重装应用程序后问题得到解决。
25. 实验问题:在Eclipse集成开发环境中编写代码时字体太小不利于观察。
解决方法:点击Window->Preferences->在搜索栏中输入font->General-> Appearance->Colors and Fonts->Basic->Text Font->点击Edit进行字体的设置->点击确认即完成了字体大小的设置。
26. 实验问题:编写Java代码进行中文输出时出现了乱码现象。
解决方法:鼠标右击->Run As->Run Configurations->Common->在Other后填写gbk然后点击Run进行运行后中文可以正常输出。
27. 实验问题:建立类后无法运行,显示没有主程序。
解决方法:填写主类运行信息语句public static void main(String[] args)或者继续在包内新建一个主类,通过类组合的形式进行类的运行。
28. 解决方法:在调用一些类的成员变量的时候显示错误。
解决方法:所调用的类成员变量为私有类型,私有类型只能在类内访问,类外无法对其直接进行访问,在类内构造公有函数形成一个对外接口,在其他类内直接通过调用这个函数来访问其类内部的私有成员即可。
五、心得体会
1、熟悉常见的Linux命令对于操作系统的使用至关重要,通过实践和练习,掌握了许多不同的Linux命令,并意识到了它们对于管理和操作系统的重要性。
2、使用man命令来查阅命令的手册页是解决问题的有效方式,手册页提供了命令的详细使用方法和参数说明,通过仔细阅读手册页,我能够更好地理解命令的功能和用法。
3、合理使用权限管理命令,可以确保系统的安全性。
4、通过使用>、>>和|等操作符,我可以将命令的输出导入到文件中,或者将多个命令连接起来以实现更复杂的数据流操作。
5、通过使用ls、cp、mv和rm等命令,我能够快速查看、复制、移动和删除文件,使文件管理变得更加高效。
6、通过使用find和locate等命令,我能够在文件系统中快速定位并找到我需要的文件,节省了查找的时间和精力。
7、使用top、htop和df等命令,我可以实时监控系统的CPU、内存和磁盘使用情况,及时调整系统配置。
8、使用命令历史和快捷键,我可以快速访问最近使用的命令,提高了操作的便捷性和效率。
9、在使用单引号和双引号的时候要特别注意,输出单个字符时可以使用单引号,如果同时输出多个字符时只能使用双引号进行输出,不然会报错。
10、在使用一些标点符号的时候要特别注意,代码内的标点符号均为英文,所以在注释和写代码切换的时候要特别的注意。