《大数据技术原理与应用》实验报告二 熟悉常用的HDFS操作
目 录
一、实验目的
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读取
*/
@Override
public 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 handler
FsURLStreamHandlerFactory 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、在使用一些标点符号的时候要特别注意,代码内的标点符号均为英文,所以在注释和写代码切换的时候要特别的注意。
更多推荐
所有评论(0)