文章目录
Hadoop是使用Java语言编写的,因此使用Java API操作Hadoop文件系统,HDFS Shell本质上就是对Java API的应用,通过编程的形式,操作HDFS,其核心是使用HDFS提供的Java API构造一个访问客户端对象,然后通过客户端对象对HDFS上的文件进行操作(增,删,改,查)

Hadoop 整合了众多文件系统,HDFS只是这个文件系统的一个实例

FileSystem 对象的一些方法可以对文件进行操作

现在D盘创建一个空的文件夹,HDFS02用来存放hadoop项目

点击左上角,新建项目

选择Maven包管理,记住一定是jdk1.8版本的,然后那个从archetype 不要选择

然后点击下一步,位置选择刚刚D盘创建的那个HDFS02文件夹,然后点击完成

创建完成之后,进来是一个pom.xml文件

添加以下的相关的hadoop和junit依赖配置,其中大部分都是有的,主要是<dependencise></dependencise> 这个部分
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>HDFS02</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>18</maven.compiler.source>
<maven.compiler.target>18</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<!--hadoop客户端-->
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.3.4</version>
</dependency>
<!--单元调试框架-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
</dependencies>
</project>
然后右上角有一个m的图标,点击一下,加载配置文件,导入包,这个很关键,不然那些包都用不起

Manen Repository (Maven仓库) http://mvnrepository.com/

搜索 hadoop
点击 hadoop-client 超链接,然后点击下面的3.3.4

就可以看到上面在Java里面配置的hadoop的依赖就是这个地方的

在resources目录里创建log4j.properties文件

把下面的配置添加进去

log4j.rootLogger=stdout, logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/hdfs.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
在 /ied01 目录里创建hadoop.txt 文件
输入命令:hdfs dfs -mkdir /ied01/hadoop.txt

在webUI界面进行查看

创建net.aex.hdfs包,在包里创建CreateFileOnHDFS类

create1()方法注意导入包的时候一定不要导错了,有些很相似

package net.aex.hdfs;
import org.apache.hadoop.conf.Configuration;
import java.net.URI;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
//下面这两个是错的
//import java.nio.file.FileSystem;
//import java.nio.file.Path;
public class CreateFileOnHDFS {
public static void create1() throws Exception{
//创建配置对象
Configuration conf = new Configuration();
//定义统一资源标识符 (uri: uniform resource identifer)
String uri = "hdfs://master:9000";
//创建文件系统对象(基于HDFS的文件系统)
FileSystem fs = FileSystem.get(new URI(uri),conf);
//创建路径对象
Path path = new Path(uri + "/ied01/hadoop.txt");
//基于路径对象创建文件
boolean result = fs.createNewFile(path);
//根据返回值判断文件是否创建成功
if(result){
System.out.println("文件[" + path + "创建成功]");
}else{
System.out.println("文件[" + path + "创建失败]");
}
}
public static void main(String[] args) throws Exception{
create1();
}
}
下面使用main方法 对CreateFileOnHDFS 函数方法进行调用

运行程序查看结果,创建失败,因为我们之前已经在linux本地 hdfs上创建了这个文件

此时将这个创建的文件路径改为 /ied02/hadoop02.txt 就成功了

在webUI界面上进行查看

注意:在/ied01 目录里确实创建了一个0字节的hadoop02.txt文件,有点类似于Hadoop shell 里执行 hdfs dfs -touchz /ied01/hadoop02.txt 但是在linux上面重复执行不会失败,只是会不断改变这个文件的时间戳,但是在Java API里面操作,如果重复执行就会失败
编写create2() 方法,事先判断文件是否已经存在
//create2
public static void create2() throws Exception{
//创建配置对象
Configuration conf = new Configuration();
//定义统一资源标识符 (uri: uniform resource identifer)
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 + "创建失败!");
}
}
}
使用main方法调用create2() 函数方法,查看程序运行结果,提示文件已经存在

此时我们怎么才能出现文件创建失败的情况呢,我们故意让HDFS进入安全模式(只能读,不能写)
在linux上删除已经创建的/ied01/hadoop02.txt 文件
输入命令: hdfs dfs -rm /ied01/hadoop02.txt

输入命令:hdfs dfsadmin safemode enter 进入安全模式

此时,再运行程序,抛出SafeModelException 异常

修改程序,来处理这个可能会抛出的安全模式异常
使用try catch 来抛出捕获异常

运行程序,查看结果

linux 输入命令:hdfs dfsadmin -safemode leave 关闭安全模式

再运行程序,查看结果 创建成功

在net.aex.hdfs 包里创建WriteFileOnHDFS 类

在linux本地 hdfs /ied01目录创建hello.txt文件

在Java 里创建 write1() 函数方法
package net.aex.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import java.net.URI;
import java.nio.charset.StandardCharsets;
public class WriteFileOnHDFS {
public static void write1() throws Exception{
//创建配置对象
Configuration conf = new Configuration();
//定义统一资源标识符 (uri: uniform resource identifer)
String uri = "hdfs://master:9000";
//创建文件系统对象(基于HDFS的文件系统)
FileSystem fs = FileSystem.get(new URI(uri),conf);
//创建路径对象
Path path = new Path(uri + "/ied01/hello.txt");
//创建文件系统数据字节输出流
FSDataOutputStream out = fs.create(path);
//通过字节输出流向文件写入数据
out.write("Hello hadoop world".getBytes());
//关闭输出流
out.close();
//关闭文件系统对象
fs.close();
}
public static void main(String[] args) throws Exception{
write1();
}
}
运行程序,查看结果,报错,没有数据节点可以写入数据

修改代码,添加一个 设置数据节点主机名属性

运行1程序,查看结果

在webUI界面上查看hello.txt 文件

在Java项目根目录创建一个文本文件test.txt

在Java WriteFileOnHDFS类里面创建 write2函数方法

//write2
public static void write2() throws Exception{
//创建配置对象
Configuration conf = new Configuration();
//设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname","true");
//定义统一资源标识符 (uri: uniform resource identifer)
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.getBytes());
}
//关闭文件系统字节输出流
out.close();
//关闭缓冲字符输入流
br.close();
//关闭文件字符输入流
fr.close();
//提示用户写入文件成功
System.out.println("本地文件[test.txt]成功写入[" + path + "]!");
}
main函数调用write2() 方法,查看结果 写入成功

其实这个方法的功能就是将本地文件复制(上传)到HDFS,有更简单的处理方法,通过使用一个工具类IOUtils来完成文件的相关操作

//write2_2()
public static void write2_2() throws Exception{
//创建配置对象
Configuration conf = new Configuration();
//设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname","true");
//定义统一资源标识符 (uri: uniform resource identifer)
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");
//利用IOUtils类提供的字节拷贝方法来复制文件
IOUtils.copyBytes(in,out,conf);
//关闭文件字节输入流
in.close();
//关闭文件系统字节输入流
out.close();
//关闭文件系统
fs.close();
//提示用户写入文件成功
System.out.println("本地文件[test.txt]成功写入[" + path + "]!");
}
使用main方法,运行write2_2() 函数方法 查看结果 写入成功

在linux查看hdfs 目录 /ied01/test.txt内容

相当于Shell里的两个命令:hdfs dfs -cat 和 hdfs dfs -get
在net.aex.hdfs包里创建ReadFileOnHDFS类
准备读取 /ied01/test.txt 文件


package net.aex.hdfs;
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 java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.net.URI;
public class ReadFileOnHDFS {
public static void read1() throws Exception{
//创建配置对象
Configuration conf = new Configuration();
//设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname","true");
//定义统一资源标识符 (uri: uniform resource identifer)
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);
//创建缓冲字符输入流对象,提高读取效率(字节流-->字符流-->缓冲流)
BufferedReader br = new BufferedReader(new InputStreamReader(in));
//定义行字符串
String nextLine = "";
// 通过循环读取缓冲字符输入流
while ((nextLine=br.readLine())!=null){
//在控制台输出读取的行内容
System.out.println(nextLine);
}
//关闭缓冲字符输入流
br.close();
//关闭文件系统数据字节输入流
in.close();
//关闭文件系统
fs.close();
}
public static void main(String[] args) throws Exception{
read1();
}
}
使用main方法调用 read1() 函数方法 查看结果 将/ied01/test.txt里的文件读取出来了

其实我们可以使用IOUtils类来简化代码,创建read_()函数方法

//read1_2()
public static void read1_2() throws Exception{
//创建配置对象
Configuration conf = new Configuration();
//设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname","true");
//定义统一资源标识符 (uri: uniform resource identifer)
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);
//读取文件在控制台显示
IOUtils.copyBytes(in,System.out,4096,false);
//关闭文件系统数据字节输入流
in.close();
//关闭文件系统
fs.close();
}
使用main方法,调用read1_2() 函数方法 查看结果

任务:将/ied01/test.txt 下载到项目下download目录里
read2()方法//read2()
public static void read2() throws Exception{
//创建配置对象
Configuration conf = new Configuration();
//设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname","true");
//定义统一资源标识符 (uri: uniform resource identifer)
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("D:\\HDFS02\\.idea\\download\\exam.txt");
//读取HDFS文件(靠输入流),写入本地文件(靠输出流)
IOUtils.copyBytes(in,out,conf);
//关闭文件系统数据字节输入流
in.close();
//关闭文件字节输出流
in.close();
//关闭文件系统
fs.close();
//提示用户文件下载成功
System.out.println("文件["+path+"]下载到本地文件[download/exam.txt]!");
}
使用main方法,调用read2()函数,查看结果 exam.txt 文件已经下载到本地的download目录下了

任务:将/ied01 目录更名为 /lzy01
renameDir() 方法
package net.aex.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 RenameDirOnFile {
public static void renameDir() throws Exception{
//创建配置对象
Configuration conf = new Configuration();
//设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname","true");
//定义统一资源标识符 (uri: uniform resource identifer)
String uri = "hdfs://master:9000";
//创建文件系统对象(基于HDFS的文件系统)
FileSystem fs = FileSystem.get(new URI(uri),conf);
//创建源路径对象
Path sourcePath = new Path("/ied01");
//创建目标路径对象
Path targePath = new Path("/lzy01");
//利用文件系统对象重命名目录
fs.rename(sourcePath,targePath);
//关闭文件系统
fs.close();
//提示用户目录更名成功
System.out.println("目录["+sourcePath.getName()+"]更名为目录[" + targePath.getName() + "]!");
}
public static void main(String[] args) throws Exception{
renameDir();
}
}
使用main方法调用renameDir()函数,查看结果 成功从/ied01 改为 lzy01

在webUI界面进行查看

任务:将/lzy01目录下的hello.txt重名为hi.txt

主要需要两个对象的路径然后通过文件系统.rename()方法,把两个文件路径放进去进行更改
public static void renameFile() throws Exception{
//创建配置对象
Configuration conf = new Configuration();
//定义统一资源标识符 (uri: uniform resource identifer)
String uri = "hdfs://master:9000";
//创建文件系统对象(基于HDFS的文件系统)
FileSystem fs = FileSystem.get(new URI(uri),conf);
//创建源路径对象
Path sourcePath = new Path("/lzy01/hello.txt");//目标对象
//创建目标路径对象(指向文件)
Path targePath = new Path("/lzy01/hi.txt");
//利用文件系统重命名文件
fs.rename(sourcePath,targePath);
//关闭文件系统
fs.close();
//提示用户更更名成功
System.out.println("文件[" + sourcePath.getName() + "]更名文件[" + targePath.getName() + "]!");
}
使用main方法调用renameFile()方法,查看结果 更名成功


在net.aex.hdfs 包里创建ListHDFSFiles类
任务:显示/lzy01 目录下的文件列表

package net.aex.hdfs;
import jdk.jshell.execution.LoaderDelegate;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.LocatedFileStatus;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.RemoteIterator;
import java.net.URI;
public class ListHDFSFile {
public static void list1() throws Exception{
//设置配置对象
Configuration conf = new Configuration();
//设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname","true");
//定义统一资源标识符 (uri: uniform resource identifer)
String uri = "hdfs://master:9000";
//创建文件系统对象(基于HDFS的文件系统)
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()){
System.out.println(ri.next());
}
}
public static void main(String[] args) throws Exception{
list1();
}
}
使用main 方法,调用list1() 方法 查看结果 这些hdfs /lzy01 目录下的文件都读取出来了
上述文件状态对象封装的有关信息,可以通过相应的方法来获取,比如getPath() 方法就可以获取路径信息,getLen()方法就可以获取文件长度信息
//list2()
public static void list2() throws Exception{
//创建配置对象
Configuration conf = new Configuration();
//设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hosename","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() + "字节");
}
}
使用main方法调用list2() 方法,查看结果 /lzy01目录下的文件的字节都显示出来了

任务:获取/lzy01/hadoop-3.3.4.tar.gz文件块信息
hadoop压缩包会分割成6个文件块

在net.aex.hdfs包里创建GetBlockLocations类
我有一个使用SeleniumWebdriver和Nokogiri的Ruby应用程序。我想选择一个类,然后对于那个类对应的每个div,我想根据div的内容执行一个Action。例如,我正在解析以下页面:https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=puppies这是一个搜索结果页面,我正在寻找描述中包含“Adoption”一词的第一个结果。因此机器人应该寻找带有className:"result"的div,对于每个检查它的.descriptiondiv是否包含单词“adoption
我正在我的Rails项目中安装Grape以构建RESTfulAPI。现在一些端点的操作需要身份验证,而另一些则不需要身份验证。例如,我有users端点,看起来像这样:moduleBackendmoduleV1classUsers现在如您所见,除了password/forget之外的所有操作都需要用户登录/验证。创建一个新的端点也没有意义,比如passwords并且只是删除password/forget从逻辑上讲,这个端点应该与用户资源。问题是Grapebefore过滤器没有像except,only这样的选项,我可以在其中说对某些操作应用过滤器。您通常如何干净利落地处理这种情况?
在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.
a=[3,4,7,8,3]b=[5,3,6,8,3]假设数组长度相同,是否有办法使用each或其他一些惯用方法从两个数组的每个元素中获取结果?不使用计数器?例如获取每个元素的乘积:[15,12,42,64,9](0..a.count-1).eachdo|i|太丑了...ruby1.9.3 最佳答案 使用Array.zip怎么样?:>>a=[3,4,7,8,3]=>[3,4,7,8,3]>>b=[5,3,6,8,3]=>[5,3,6,8,3]>>c=[]=>[]>>a.zip(b)do|i,j|c[[3,5],[4,3],[7,6],
我有一个非常简单的Controller来管理我的Rails应用程序中的静态页面:classPagesController我怎样才能让View模板返回它自己的名字,这样我就可以做这样的事情:#pricing.html.erb#-->"Pricing"感谢您的帮助。 最佳答案 4.3RoutingParametersTheparamshashwillalwayscontainthe:controllerand:actionkeys,butyoushouldusethemethodscontroller_nameandaction_nam
1、接口请求基本操作1.1例子tips在view的选项可以zoomin调整窗口字帖大小。1、创建一个测试的workspace,并命名为test2、test后面新增一个addrequest3、选择发送GET,URL为一个开源的https://api.apiopen.top/api/sentences获取每日一句4、点击send查看内容Tips:如果提示出现Error:tunnelingsocketcouldnotbeestablished,statusCode=407错误,参照以下解决办法)关于tunnelingsocketcouldnotbeestablished,cause=getaddri
Linux操作系统——网络配置与SSH远程安装完VMware与系统后,需要进行网络配置。第一个目标为进行SSH连接,可以从本机到VMware进行文件传送,首先需要进行网络配置。1.下载远程软件首先需要先下载安装一款远程软件:FinalShell或者xhell7FinalShellxhell7FinalShell下载:Windows下载http://www.hostbuf.com/downloads/finalshell_install.exemacOS下载http://www.hostbuf.com/downloads/finalshell_install.pkg2.配置CentOS网络安装好
Ruby语言是否可以用于创建全新的移动操作系统或桌面操作系统,即是否可以用于系统编程? 最佳答案 嗯,现在有一些操作系统使用比C更高级的语言。基本上,ruby解释器本身需要用一些低级的东西来编写,并且需要一些引导加载代码将功能齐全的ruby解释器作为独立内核加载到内存中。一旦ruby解释器被引导并以内核模式(或innerrings之一)运行,就没有什么可以阻止您在其上构建整个操作系统。不幸的是,它可能会很慢。每个操作系统功能的垃圾收集可能会相当引人注目。ruby解释器将负责任务调度和网络堆栈等基本事情,使用垃圾收集框架会大大
假设我们有以下描述一个人的JSON对象:{"firstName":"John","lastName":"Smith","age":25,"address":{"streetAddress":"212ndStreet","city":"NewYork","state":"NY","postalCode":"10021"},"phoneNumber":[{"type":"home","number":"212555-1234"},{"type":"fax","number":"646555-4567"}]有人可以建议在Rails3中操作前一个对象的最优雅和最有效的方法吗?我希望能够:添加另
我按照Cormen的“算法导论”中的伪代码,在Ruby中创建了简单的插入排序实现:defsort_insert(array)(1...array.length).eachdo|item_index|key=array[item_index]i=item_index-1whilei>=0&&array[i]>keydoarray[i+1]=array[i]i-=1endarray[i+1]=keyendarrayend它有效,但执行速度非常慢。对于约20k个元素的数组array=((0..10_000).to_a*2).shuffle,排序大约需要20秒。我只测量这个方法调用的时间,没有