☰
HDFS读写实操:结合Eclipse与Hadoop实现文件合并上传与下载
2026/10/5 5:02:30 网站建设 项目流程

简介:这是《云计算技术》课程实验四“HDFS文件的读写”的完整实验报告,面向计算机专业学生和Hadoop入门者,用于掌握HDFS文件上传、下载与合并的编程实现。报告以PDF格式呈现,共1个文件,压缩包大小约1.1MB,内容精炼,适合作为课程实验参考或复习材料。目前已有561人学习下载,说明该主题在同类作业与实践中需求较高。报告不仅记录了安装Eclipse、配置hadoop-eclipse-plugin连接Hadoop集群的过程,还围绕PutMerge与GetMerger两个功能展开,涉及FileSystem API调用、本地文件读取与HDFS写入、listStatus()遍历目录并合并下载等关键知识点;同时复盘了环境配置中常见的路径设置、插件版本兼容等问题及排错思路。读者可借此快速理解HDFS读写流程,减少实验踩坑时间,也能为后续MapReduce开发打下基础。

1. HDFS 文件的读写实验:从 PutMerge 和 GetMerge 看一次完整的 HDFS 落地

这份《云计算技术》实验四的实验报告成绩是满分,但真正值钱的不是那个分数,而是它一句话带过的坎坷:“在将 Eclipse 与 Hadoop 连接起来的时候,花了蛮多功夫”。这句话几乎复现了每个第一次做 HDFS 读写的人的真实路径:装 Eclipse、放插件、配视图、建项目,最后才是写代码。前面每一步看着都是“下一步”,实际踩下去全是坑。这门课的核心目标是让你动手实现 PutMerge 和 GetMerge 两个功能,也就是把本地多个文件合并后上传 HDFS、把 HDFS 上一个目录里的所有文件合并下载到本地。这篇笔记就把实验报告里的步骤拆开,讲清每步为什么这么做、参数怎么填、代码怎么跑、翻车了看哪里。适合云计算课程学生、刚接触 Hadoop 的开发者,以及想在本地复现一遍 HDFS 读写流程的人。

2. Eclipse 连接 Hadoop 的实操:插件目录、端口和项目类型三个关键点

2.1 hadoop-eclipse-plugin-2.10.1.jar 的安装:从放 jar 到确认加载

Eclipse 的插件机制核心在 plugins 目录,但这不代表把 jar 丢进去就会被加载。实验报告里写的是“放到 plugins 中”,实际第一次做的人会在这里翻车——装完重启 Eclipse,Window 菜单里根本没有 Hadoop 相关选项。

先检查插件位置和权限:

ls -l /opt/eclipse/plugins/hadoop-eclipse-plugin-2.10.1.jar

如果列表里连文件都看不到,说明路径本身不对。常见做法是先确认你 Eclipse 的安装目录下有 plugins 文件夹;用软件管家这类工具装的 Eclipse,plugins 可能在安装目录的下一层,不能想当然。这一步还有一个容易被忽略的问题:jar 的属主和权限。Eclipse 是以当前用户身份启动的,如果 jar 是 root 拷贝进去的,普通用户的目录权限不够,插件会被静默跳过,连日志都不报。

报告里用的插件版本是 2.10.1,它对应 Hadoop 2.x 生态。如果你的本机 Hadoop 不是 2.x,或者 Eclipse 版本太新,这个 jar 很可能装不上。判断依据很简单:

hadoop version

常见做法是先看输出的 Hadoop 版本号,再去找匹配的插件,不要拿着一个 jar 到处试。版本不匹配时,就算插件能被加载,后面新建 Map/Reduce Project 也可能出现类加载冲突,报错信息五花八门。

确认插件文件在位之后,用-clean参数重启 Eclipse。这一步经常被忽略,但确实是最有效的加载手段。Eclipse 的插件注册信息有缓存,普通重启看到的还是旧状态;-clean会强制清理 osgi 缓存并重新扫描插件。

/opt/eclipse/eclipse -clean &

-clean属于“你一旦知道就不会忘”的实用技巧,实验报告里不会写,但在插件加载失败时,它就是最优先的排查手段。如果这样还是看不到 Hadoop 相关菜单,那就不是缓存问题,而是版本兼容。常见做法是换用 Eclipse 4.7 或更早的版本,同时保持 JDK 1.8。新版 Eclipse 对这类插件式集成不再友好,换版本往往比改配置省力得多。

2.2 Map/Reduce 配置面板:安装目录、端口和 hdfs-site.xml 的关系

插件加载成功后,要把 Hadoop 安装目录和连接信息填进去。具体路径是 Window → Preferences → Hadoop Map/Reduce → Browse,选择一个 bin、lib、etc 都齐全的目录。这里选错目录有个典型后果:Eclipse 能打开 Map/Reduce 透视图,但一运行就报找不到 Configuration 类。原因很简单,插件是根据这个目录去加载 Hadoop 的 jar,不是靠“认出”目录,选错的话后面的类路径就是空的。

连接信息在 Window → Show View → Other → Map/Reduce Location 里。右键新建 Location,需要填 Master 和 DFS 地址。本机伪分布式环境通常这样填:

配置项值说明
Map/Reduce Master hostlocalhost本机运行 Hadoop 时填 localhost
Map/Reduce Master port9000对应 core-site.xml 里的 fs.defaultFS
DFS Master hostlocalhost和 Master 相同
DFS Master port9000HDFS 的 RPC 端口,不是 50070
Hadoop installation directory/usr/local/hadoop要包含 bin 和 etc 目录

这里最容易错的是端口。HDFS 的 Web UI 端口是 50070 或 9870,但填到 Location 里的是 RPC 端口,两者原理不同,填了 UI 端口一定连不上。我一般会先跑一条命令读配置文件:

hdfs getconf -confKey fs.defaultFS

输出一般是hdfs://localhost:9000,那 Eclipse 里就填 9000。如果输出是别的端口,以这条命令为准,不要自己猜。写完 Location 后出现文件系统树状视图,说明连接基本通了;如果整棵树是空的,先右键 Refresh,很多版本的插件不会自动刷新目录。

2.3 新建 Map/Reduce Project:项目向导、构建路径和 JDK 版本

实验报告里写的是“新建一个 map/reduce project”。实际做的时候会在 New 向导里看到 Map/Reduce Project 这一项,它是否出现取决于 2.1 节插件有没有加载成功。如果向导里一直没有这一项,说明插件有问题,别硬建,回到 2.1 的-clean再查。

项目建好后,Eclipse 会把 Hadoop 的 jar 加到构建路径。此时可以直接写类。但有个坑:Map/Reduce Project 的依赖是插件提供的,Eclipse 里可能不显示红叉,但用 hadoop jar 提交运行时,就暴露类找不到的问题。我的习惯是建完项目先看 Java Build Path → Libraries,确认里面有没有 hadoop-common 和 hadoop-hdfs 这两个关键 jar;没有就手动 Add JARs 加进去。

还有一种情况是老师机器上的 Hadoop 装在其他目录,学生自己的电脑想复现实验,又不想装完整 Hadoop。这时可以只准备一个干净的 Hadoop 安装包解压目录,甚至先用本地文件系统验证代码逻辑。但实验要求“从云端下载”,显然要连真 HDFS,所以还是先把伪分布式启动起来再写代码。

启动后可以用 jps 验证必要进程:

jps

至少要有 NameNode 和 DataNode 两个进程,否则后续所有读写都会报连接异常。这一步经常被跳过,因为 Eclipse 配置成功不代表 Hadoop 在运行。另外,Map/Reduce Project 的 Java 编译级别建议设成 1.8。Hadoop 2.x 在 JDK 11 以上的环境里经常出各种奇怪的 SecurityException,不是代码问题,而是版本组合不对;在项目的 Java Compiler 里选 1.8 能省掉一批莫名其妙的报错。

3. PutMerge 与 GetMerge 的编程实现:FileSystem API 选型与代码逐段拆解

3.1 动手前先分清两类 FileSystem 和 listStatus 的返回值

HDFS 读写的核心类是org.apache.hadoop.fs.FileSystem,它是一个抽象类。FileSystem.get(conf)会根据 conf 里的fs.defaultFS返回具体实现:连接hdfs://时返回 DistributedFileSystem,连接file://时返回 LocalFileSystem。这个特点直接影响 PutMerge 怎么写——源文件在本地,目标在 HDFS,就需要同时拿到两个 FileSystem 实例。

FileSystem local = FileSystem.getLocal(conf); FileSystem hdfs = FileSystem.get(conf);

实验报告里强调的 GetMerge,需要把 HDFS 某个目录下所有文件取出来下载到本地,核心调用是listStatus(),它返回FileStatus[]。FileStatus.isDirectory()可以过滤目录,但 HDFS 上还会有_SUCCESS这类隐藏标记文件,合并时不过滤的话,结果里会混进空文件或临时内容。

读写用的流是FSDataInputStream和FSDataOutputStream,用法和普通 Java 流几乎一样,区别只在打开方式:读用fs.open(path),写用fs.create(path, true),第二个参数表示是否允许覆盖。

3.2 PutMerge:把本地目录合并成一个文件上传 HDFS

下面这段是我在 Eclipse 里跑通的版本,核心逻辑是先列出本地目录,再把每个文件追加写入同一个 HDFS 目标文件:

import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FSDataOutputStream; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; public class PutMerge { public static void main(String[] args) throws IOException { Configuration conf = new Configuration(); conf.set("fs.defaultFS", "hdfs://localhost:9000"); FileSystem local = FileSystem.getLocal(conf); FileSystem hdfs = FileSystem.get(conf); Path localDir = new Path("/home/student/data"); Path hdfsFile = new Path("/user/student/merged.txt"); FSDataOutputStream out = hdfs.create(hdfsFile, true); FileStatus[] files = local.listStatus(localDir); for (FileStatus file : files) { if (file.isFile()) { FSDataInputStream in = local.open(file.getPath()); byte[] buffer = new byte[4096]; int len; while ((len = in.read(buffer)) > 0) { out.write(buffer, 0, len); } in.close(); } } out.close(); System.out.println("PutMerge 完成:" + hdfsFile); } }

几个参数要展开说。local.listStatus(localDir)用的是本地文件系统,所以路径要写成/home/student/data这种本地路径,不能写成hdfs://开头的绝对路径。hdfs.create(hdfsFile, true)里的第二个参数是true表示允许覆盖;第一次跑建议改成false,万一重名会立刻报错,反而能提醒你目标路径是不是写错了。

缓冲区的 4096 是字节数,比 1024 能少很多次循环,也不会占用太多内存。文件特别大时可以调到 8192 或 16384。还要注意,HDFS 上如果/user/student这个目录不存在,create会直接报找不到父目录,所以跑代码前先执行一次:

hdfs dfs -mkdir -p /user/student

3.3 GetMerge:从 HDFS 目录下载所有文件并合并到本地

GetMerge 的方向正好相反,源目录在 HDFS,目标文件在本地。要过滤目录和隐藏文件,过滤条件比 PutMerge 更严格:

import java.io.FileOutputStream; import java.io.OutputStream; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; public class GetMerge { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); conf.set("fs.defaultFS", "hdfs://localhost:9000"); FileSystem hdfs = FileSystem.get(conf); Path hdfsDir = new Path("/user/student/input"); Path localFile = new Path("/home/student/output/merge.txt"); OutputStream out = new FileOutputStream(localFile.toString()); FileStatus[] status = hdfs.listStatus(hdfsDir); for (FileStatus st : status) { if (!st.isDirectory() && !st.getPath().getName().startsWith("_")) { FSDataInputStream in = hdfs.open(st.getPath()); byte[] buffer = new byte[4096]; int len; while ((len = in.read(buffer)) > 0) { out.write(buffer, 0, len); } in.close(); } } out.close(); System.out.println("GetMerge 完成:" + localFile); } }

st.getPath().getName().startsWith("_")是过滤隐藏文件的关键。很多教程只写!st.isDirectory(),但 HDFS 上的_SUCCESS、_temporary都以下划线开头,不过滤的话合并结果会多出空文件或临时文件内容。实验里如果只手动 put 文件上去,可能没有这些标记,但保留这个判断能少踩一次坑。

这里用FileOutputStream写本地文件,没有用 Hadoop 的FSDataOutputStream,因为目标在本地文件系统,没必要绕回 Hadoop 流体系。运行前记得创建本地输出目录:

mkdir -p /home/student/output

否则new FileOutputStream会因为父目录不存在直接抛 FileNotFoundException。

3.4 运行方式:Eclipse 直接跑与 hadoop jar 提交的差别

Map/Reduce Project 里写的主类可以直接右键 Run As → Java Application 运行。好处是调试方便,System.out直接打在控制台,报错能立刻看到堆栈。缺点是它实际只是一个调 HDFS API 的普通 Java 程序,并不经过 MapReduce 作业框架,所以在 ResourceManager 界面上看不到这个任务,这是正常的。

如果老师要求提交 jar 包,可以 Export → Runnable JAR File 打包,再用 hadoop 命令运行:

hadoop jar experiment.jar GetMerge /user/student/input /home/student/output/merge.txt

命令行方式比 Eclipse 直跑麻烦的地方在 classpath。hadoop jar会带上 Hadoop 自身的依赖,但如果你额外用了第三方库,就得用-libjars手动指定:

hadoop jar experiment.jar GetMerge /user/student/input /home/student/output/merge.txt -libjars yourlib.jar

Eclipse 里看不到这个问题,因为插件已经把这些依赖塞进了构建路径。等打包再跑才发现,相当于给实验报告预留了“演示时翻车”的坑。

提示:如果你是在 Windows 的 Eclipse 里跑这段代码,第一次经常会报 “Failed to locate the winutils binary in the hadoop binary path”。这不是代码问题,而是 Hadoop 在 Windows 下需要 winutils.exe 配合。常见做法是下载与 Hadoop 版本对应的 winutils.exe,放到 HADOOP_HOME\bin 下,或在程序里设置 System.setProperty("hadoop.home.dir", "你的解压目录")。

4. HDFS 读写实验避坑:五个高频问题的现象、原因与处理

4.1 现象:Eclipse 里长期找不到 Map/Reduce 视图

现象:Window → Show View → Other 里输入 map/reduce 搜索不到结果,或者 Window → Preferences 里没有 Hadoop Map/Reduce 选项。

原因分两种。第一是插件 jar 没真正被加载,可能放错目录,也可能 Eclipse 缓存没刷新;第二是 Eclipse 版本太新,老插件本身不兼容。多数人卡住,是因为只做了“放 jar 和重启”,没做缓存清理。

处理:先确认 jar 在 plugins 目录,再带-clean参数启动 Eclipse。如果这样还是没有,就换一个和插件版本配套的 Eclipse,常见做法是用 Eclipse 4.7 左右版本。如果不想折腾插件,可以退到普通 Java 项目 + Maven 依赖,Maven 方式更可控,只是不符合“老师要求用 Map/Reduce Project”的场景。

4.2 现象:连接 localhost:9000 超时或不稳定

现象:新建 Map/Reduce Location 后点连接,界面卡住很久,最后报无法连接到 master 或者 connection refused。

原因:绝大多数是端口填错。HDFS 的 RPC 端口通常是 9000 或 8020,取决于 core-site.xml 里fs.defaultFS的值。但不少教程会提到 50070 或 9870,那是 NameNode Web UI 端口,HTTP 端口不能拿来建 RPC 连接。

处理:先读配置,再填 Eclipse:

hdfs getconf -confKey fs.defaultFS

如果输出是hdfs://localhost:9000,Eclipse 里就填 9000;如果是 8020,就填 8020。顺手确认进程:

jps

没有 NameNode 进程的话,所有连接尝试都会失败。这个现象看起来像玄学,其实每一步都有日志可查,先启动集群再谈配置。

4.3 现象:读文件正常、写文件报 Permission denied

现象:GetMerge 下载能跑,PutMerge 一写就报Permission denied: user=student, path=/user/student/merged.txt,或者用 root 启动 Hadoop 后其他用户无权限。

原因:HDFS 权限检查默认开启。NameNode 收到的用户名来自当前系统用户,如果 HDFS 目录所有者是 root,而当前用户是 student,写入就会被拒绝。伪分布式环境里很常见,因为不少人习惯用某个固定用户启动集群。

处理:把目录所有者改成当前用户:

hdfs dfs -mkdir -p /user/student hdfs dfs -chown -R student:student /user/student

如果集群是在 root 下启动的,也可以用HADOOP_USER_NAME环境变量临时指定身份:

HADOOP_USER_NAME=root hdfs dfs -put local.txt /user/student/

我一般优先改目录所有者,而不是冒充 root,因为后面 Eclipse 里的用户名也要一起读写,统一身份能少很多边界问题。

4.4 现象:合并结果为空文件或文件数量不对

现象:GetMerge 跑完,本地 merge.txt 存在但大小是 0,或者明显少了某个文件。

原因:最常见的是输出流没有及时 flush/close,数据还留在缓冲区,程序退出时被丢弃,看起来是“运行成功但结果为空”。另一个原因是 listStatus 遍历时没过滤目录,或者循环里把同一个路径写死了,导致某个文件被读多次、其他文件一次都没读。

处理:循环结束后先 flush 再 close,并确认每个文件都打开独立的输入流:

out.flush(); out.close();

再把源目录列出看看:

hdfs dfs -ls -R /user/student/input

如果里面有_开头的文件,回到 3.3 节的过滤逻辑。这个动作成本极低,但能避免对“文件数量不对”做无意义的反复猜测。

4.5 现象:合并后字节数不一致或出现空行

现象:本地原始文件加起来是 1200 字节,合并后变成 1225 字节;或者文本文件中间多出空行,拼接处看起来有两个换行。

原因:如果源文件是文本文件,最后一个字符没有换行,合并时两个文件直接拼接,读起来像两段文本黏在一起。反过来,Windows 上编辑的文件带\r\n换行符,和 Linux 文本混在一起后,cat 查看会多出^M,视觉上就像空行。还有一类情况是把_SUCCESS这种隐藏文件也读进去了,隐藏文件大多为空,但会把字节数搞对不上。

处理:合并要保持“原样拼接”,就不要在写入时手动加换行;如果明确要强调文件边界,可以在每个文件写完后加out.write('\n')。课程实验一般不要求加,代码里保持不加。至于^M的问题,先把源文件统一转换再合并:

dos2unix /home/student/data/*

转换后重新跑 PutMerge,字节数就正常了。

5. 验证实验结果的技巧:目录字节数对比与脚本化校验

5.1 合并前后先记录目录总字节数

HDFS 读写实验的成败不能靠肉眼判断。我习惯在跑代码前记录源目录的累计大小,跑完再核对目标文件,字节数一致才算真正写完。

hdfs dfs -du -s /user/student/input hdfs dfs -du -s /user/student/merged.txt

-du -s输出的是字节数,这里不要加-h,加了会变成人类可读单位,大小接近时很难精确比较。目录输出的第一列是累计大小,文件输出的第一列就是文件本身大小,两者相等说明所有字节都被读出来并写进去了。

5.2 把校验流程写成脚本,避免手打漏步骤

手动一行行敲命令容易漏。我会写一个三行脚本固定流程:

expected=$(hdfs dfs -du -s /user/student/input | awk '{print $1}') actual=$(hdfs dfs -du -s /user/student/merged.txt | awk '{print $1}') if [ "$expected" -eq "$actual" ]; then echo "size check ok: $actual"; else echo "size mismatch: $expected vs $actual"; fi

逻辑很简单,awk 取第一列,shell 做整数比较。第一次跑代码前记录 expected,写完代码再跑一次做对比。如果 mismatch,按第 4 章的思路查:输出流没关、隐藏文件混入、源目录路径不对。

从那以后,我每次交付 HDFS 读写相关演示前,都强制走一遍“看配置端口、查目录所有者、对字节数”这三步,不会再抱着“应该没问题吧”的心情点运行。这个习惯帮我挡掉了好几次现场翻车,希望你也能在实验之外,把这套校验动作用起来。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询