写在前面

本篇来学习如何访问HDFS文件系统,主要包括使用Shell和Java API这两种方式。

使用HDFS Shell方式

概述

HDFS提供了基于Shell的操作命令来管理HDFS上的数据,这些Shell命令和Linux系统上的命令非常相似,因此熟悉Linux的小伙伴可以快速对HDFS进行操作。

HDFS的基本命令格式为:

1
bin/hdfs dfs -cmd <args>

请注意在使用HDFS Shell之前,用户需要提前启动Hadoop。

一些常用的Shell命令

接下来学习一些HDFS Shell中常用的命令,如下所示:
(1)列出HDFS指定路径下的文件目录。其对应的命令格式如下所示:

1
hadoop fs -ls 目录路径

举个例子,查看HDFS根目录下的文件:

1
2
3
4
5
6
[hadoop@master bin]$ hadoop fs -ls /
Found 4 items
-rw-r--r-- 1 hadoop supergroup 38 2020-06-20 22:08 /hello.txt
drwxr-xr-x - hadoop supergroup 0 2020-06-20 22:05 /helloworld
drwx------ - hadoop supergroup 0 2020-06-20 22:11 /tmp
drwxr-xr-x - hadoop supergroup 0 2020-06-20 22:12 /wc_out

当然也可以使用-ls -R命令来递归查看文件,这个命令不仅会打印出目录路径下的文件,还会打印出其子目录和子目录的文件。

举个例子,查看一下/data目录下的所有文件:

1
2
[hadoop@master bin]$ hadoop fs -ls -R /data
ls: `/data': No such file or directory

那是因为当前系统中不存在/data目录。
(2)在HDFS中创建目录。其对应的命令格式如下所示:

1
hadoop fs -mkdir 目录名称

举个例子,在HDFS的根目录下创建名为datatest的目录:

1
2
3
4
5
6
7
8
[hadoop@master bin]$ hadoop fs -mkdir /datatest
[hadoop@master bin]$ hadoop fs -ls /
Found 5 items
drwxr-xr-x - root supergroup 0 2020-12-29 21:11 /datatest
-rw-r--r-- 1 hadoop supergroup 38 2020-06-20 22:08 /hello.txt
drwxr-xr-x - hadoop supergroup 0 2020-06-20 22:05 /helloworld
drwx------ - hadoop supergroup 0 2020-06-20 22:11 /tmp
drwxr-xr-x - hadoop supergroup 0 2020-06-20 22:12 /wc_out

当然,用户也可以使用-mkdir -p参数来级联创建目录。

举个例子,开发者想在HDFS上创建/datatest/envy/input目录,而envy目录之前是不存在的,因此就必须使用上面的级联创建目录:

1
2
3
4
[hadoop@master bin]$ hadoop fs -mkdir -p /datatest/envy/input
[hadoop@master bin]$ hadoop fs -ls -R /datatest
drwxr-xr-x - hadoop supergroup 0 2020-06-29 21:15 /datatest/envy
drwxr-xr-x - hadoop supergroup 0 2020-06-29 21:15 /datatest/envy/input

(3)上传文件至HDFS。其对应的命令格式如下所示:

1
hadoop fs -put 源路径 目标存放路径

举个例子,将本地Linux文件系统目录/home/hadoop/下的input.txt文件上传至Hadoop文件目录的/datatest目录下:

1
2
3
4
5
6
7
8
[hadoop@master hadoop]$ hadoop fs -ls -R /datatest
drwxr-xr-x - hadoop supergroup 0 2020-06-29 21:15 /datatest/envy
drwxr-xr-x - hadoop supergroup 0 2020-06-29 21:15 /datatest/envy/input
[hadoop@master hadoop]$ hadoop fs -put /home/hadoop/input.txt /datatest
[hadoop@master hadoop]$ hadoop fs -ls -R /datatest
drwxr-xr-x - hadoop supergroup 0 2020-06-29 21:15 /datatest/envy
drwxr-xr-x - hadoop supergroup 0 2020-06-29 21:15 /datatest/envy/input
-rw-r--r-- 1 root supergroup 13 2020-12-0621:27 /datatest/input.txt

(4)从HDFS上下载文件。其对应的命令格式如下所示:

1
hadoop fs -get HDFS中文件路径 本地存放路径

举个例子,将前面上传的input.txt文件下载到本地用户的目录下,即/home/hadoop目录:

1
2
3
[hadoop@master bin]$ hadoop fs -get /datatest/input.txt /home/hadoop/
[hadoop@master ~]$ ls
input.txt

(5)查看HDFS上某个文件的内容。其对应的命令格式如下所示:

1
2
3
hadoop fs -text HDFS上的文件存放路径
//或者
hadoop fs -cat HDFS上的文件存放路径

举个例子,查看我们之前上传的input.txt文件内的内容:

1
2
3
4
[hadoop@master ~]$ hadoop fs -text /datatest/input.txt
hello,world!
[hadoop@master ~]$ hadoop fs -cat /datatest/input.txt
hello,world!

(6)统计HDFS上各目录下文件的大小。其对应的命令格式如下所示:

1
hadoop fs -du 目录路径

举个例子,查看/datatest目录下各个文件的大小:

bin]$ hadoop fs -dulink
1
2
0   0   /datatest/envy
13 13 /datatest/input.txt

请注意上面统计的各目录下文件的大小时,使用的单位是字节
(7)删除HDFS上某个文件或者目录。其对应的命令格式如下所示:

1
2
3
hadoop fs -rm 文件存放路径
//或者
hadoop fs -rm -r 文件存放路径

请注意,在HDFS中删除文件和删除目录所使用的命令是不同的,-rm参数表示删除指定的文件或者空目录;-rm -r参数表示递归删除指定目录下的所有子目录和文件。

举个例子,删除之前上传的input.txt文件:

1
2
3
4
5
[hadoop@master bin]$ hadoop fs -rm /datatest/input.txt
Deleted /datatest/input.txt
[hadoop@master bin]$ hadoop fs -ls /datatest
Found 1 items
drwxr-xr-x - hadoop supergroup 0 2020-06-29 21:15 /datatest/envy

而删除/datatest目录下的所有子目录和文件,则使用的命令为:

1
[hadoop@master bin]$ hadoop fs -rm -r /datatest

因此在生产环境中要避免使用-rm -r参数,它会导致误删除操作。
(8)使用help命令寻求帮助。其对应的命令格式如下所示:

1
hadoop fs -help 命令

注意当开发者不知道某个命令的作用时,那么就可以使用help命令来寻求帮助,查询该命令的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[hadoop@master bin]$ hadoop fs -help ls
-ls [-d] [-h] [-R] [<path> ...] :
List the contents that match the specified file pattern. If path is not
specified, the contents of /user/<currentUser> will be listed. Directory entries
are of the form:
permissions - userId groupId sizeOfDirectory(in bytes)
modificationDate(yyyy-MM-dd HH:mm) directoryName

and file entries are of the form:
permissions numberOfReplicas userId groupId sizeOfFile(in bytes)
modificationDate(yyyy-MM-dd HH:mm) fileName

-d Directories are listed as plain files.
-h Formats the sizes of files in a human-readable fashion rather than a number
of bytes.
-R Recursively list the contents of directories.

请注意,由于此处是对HDFS操作,因此上述命令中的hadoop fs可以使用hdfs dfs命令来进行代替。也就是说用户可以使用hadoop fs -ls,也可以使用hdfs dfs -ls命令来实现列出HDFS指定路径下的文件目录这一功能。

上面介绍的都是几个较为常用的Shell命令,因此需要熟练掌握,如果还想学习其他的Shell命令操作,可以点击 这里 进行学习:

使用Java API方式

概述

用户除了使用HDFS Shell方式来访问HDFS上的数据,还可以使用Java API来操作HDFS上的数据。由于在实际的大数据开发过程中都是以代码方式来开发的,因此对于Java API方式也必须掌握。

搭建开发环境

此处使用Maven来构建Java应用,因此需要使用Maven新建一个Java项目,名称为envy-hdfs,之后在其pom.xml依赖文件中新增如下依赖信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hadoop.version>2.6.0</hadoop.version>
</properties>

<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>${hadoop.version}</version>
</dependency>

<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>${hadoop.version}</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>

由于我们在前面对HDFS的权限进行了设置,即在$HADOOP_HOME/etc/hadoop/hdfs-site.xml中添加了如下配置,因此其他用户操作会提示没有权限:

1
2
3
4
<property>
<name>dfs.permissions</name>
<value>false</value>
</property>

单元测试

在单元测试中,一般将初始化的操作放在setUp方法中,而将关闭资源的操作放在tearDown方法中来完成。出于上述考虑,笔者在测试HDFS时,将打开文件系统的操作放在setUp方法中,而将关闭文件系统的操作放在tearDown方法中。

在main包下的java包内新建com/envy/envyhdfs包,之后在envyhdfs包内新建一个app包,并在app包内新建一个HDFSApp类,该类就是使用Java来操作HDFS的API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.envy.envyhdfs.app;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.net.URI;

/**
* @描述:HDFS Java API操作
* @创建人: 余思
* @创建时间: 2020/06/29
*/
public class HDFSApp {
public static final String HDFS_PATH = "hdfs://master:9000";

Configuration configuration = null;
FileSystem fileSystem = null;

@Before
public void setUp() throws Exception {
System.out.println("*********HDFSApp.setUp()**********");
configuration = new Configuration();
fileSystem = FileSystem.get(new URI(HDFS_PATH),configuration);
}

@After
public void tearDown() throws Exception {
fileSystem = null;
configuration = null;
System.out.println("*********HDFSApp.tearDown()**********");
}
}

使用Java API操作HDFS常用操作

接下来将在前面的HDFSApp中书写代码,通过使用Java API来操作HDFS,如下所示:

(1)创建目录。先查询一下hdfs根目录下当前存有的目录信息:

1
2
3
4
5
[hadoop@master /]$ hadoop fs -ls /
Found 3 items
drwxr-xr-x - hadoop supergroup 0 2020-06-20 22:05 /helloworld
drwx------ - hadoop supergroup 0 2020-06-20 22:11 /tmp
drwxr-xr-x - hadoop supergroup 0 2020-06-20 22:12 /wc_out

接着在HDFSApp类中定义创建目录的方法mkdir:

1
2
3
4
5
@Test
public void mkdir() throws IOException {
System.out.println("********创建目录********");
fileSystem.mkdirs(new Path("/hdfsapi/test"));
}

之后执行该方法,然后查询一下此时hdfs根目录下当前存有的目录信息:

1
2
3
4
5
6
[hadoop@master /]$ hadoop fs -ls /
Found 4 items
drwxr-xr-x - Administrator supergroup 0 2020-06-30 20:19 /hdfsapi
drwxr-xr-x - hadoop supergroup 0 2020-06-20 22:05 /helloworld
drwx------ - hadoop supergroup 0 2020-06-20 22:11 /tmp
drwxr-xr-x - hadoop supergroup 0 2020-06-20 22:12 /wc_out

可以看到多出了我们创建的/hdfsapi/test目录。
(2)创建文件。在HDFSApp类中定义创建文件的方法create:

1
2
3
4
5
6
7
8
@Test
public void create() throws Exception {
System.out.println("********创建文件********");
FSDataOutputStream outputStream = fileSystem.create(new Path("/hdfsapi/test/hello.txt")) ;
outputStream.write("hello,world".getBytes());
outputStream.flush();
outputStream.close();
}

之后执行该方法,然后查询一下此时hdfs中的/hdfsapi/test目录下的信息:

1
2
3
[hadoop@master /]$ hadoop fs -ls -R /hdfsapi
drwxr-xr-x - Administrator supergroup 0 2020-06-30 20:27 /hdfsapi/test
-rw-r--r-- 3 Administrator supergroup 11 2020-06-30 20:27 /hdfsapi/test/hello.txt

可以看到此时/hdfsapi/test目录下就出现了我们创建的hello.txt文件。
(3)重命名。在HDFSApp类中定义重命名的方法rename:

1
2
3
4
5
6
7
@Test
public void rename() throws Exception {
System.out.println("********重命名********");
Path oldPath = new Path("/hdfsapi/test/hello.txt");
Path newPath = new Path("/hdfsapi/test/hello2.txt");
System.out.println(fileSystem.rename(oldPath,newPath));
}

之后执行该方法,可以看到该方法执行成功会输出true,然后查询一下此时hdfs中的/hdfsapi/test目录下的信息:

1
2
3
[hadoop@master /]$ hadoop fs -ls -R /hdfsapi
drwxr-xr-x - Administrator supergroup 0 2020-06-30 20:27 /hdfsapi/test
-rw-r--r-- 3 Administrator supergroup 11 2020-06-30 20:27 /hdfsapi/test/hello2.txt

可以看到此时/hdfsapi/test目录下的hello.txt文件就已经修改为hello2.txt文件。
(4)上传本地文件到HDFS。在HDFSApp类中定义上传本地文件到HDFS的方法copyFromLocalFile:

1
2
3
4
5
6
7
@Test
public void copyFromLocalFile() throws Exception {
System.out.println("********上传本地文件到HDFS********");
Path src = new Path("F:/Hadoop/hdfs/maven/envy-hdfs/src/main/resources/book.txt");
Path dist = new Path("/hdfsapi/test/");
fileSystem.copyFromLocalFile(src,dist);
}

请注意,由于本地是Windows系统,因此这里的src源路径必须是Windows系统上的路径。之后执行该方法,然后查询一下此时hdfs中的/hdfsapi/test目录下的信息:

1
2
3
4
[root@master sbin]# hadoop fs -ls -R /hdfsapi
drwxr-xr-x - Administrator supergroup 0 2020-07-01 10:50 /hdfsapi/test
-rw-r--r-- 3 Administrator supergroup 12 2020-07-01 10:50 /hdfsapi/test/book.txt
-rw-r--r-- 3 Administrator supergroup 11 2020-06-30 20:27 /hdfsapi/test/hello2.txt

可以看到此时/hdfsapi/test目录下就已经有了book.txt文件。
(5)查看某个目录下的所有文件。在HDFSApp类中定义查看某个目录下的所有文件的方法listFiles:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void listFiles() throws Exception {
System.out.println("********查看某个目录下的所有文件********");
FileStatus[] listStatus = fileSystem.listStatus(new Path("/hdfsapi/test"));
for(FileStatus fileStatus:listStatus){
String isDir = fileStatus.isDirectory() ? "文件夹":"文件"; //文件夹或者文件
String permission = fileStatus.getPermission().toString(); //权限
short replication = fileStatus.getReplication(); //副本系数
long length = fileStatus.getLen(); //长度
String path = fileStatus.getPath().toString(); //路径
System.out.println(isDir+"\t"+permission+"\t"+replication+"\t"+length+"\t"+path);
}
}

先使用Hadoop Shell命令查看一下此时hdfs中的/hdfsapi/test目录下的信息:

1
2
3
[root@master sbin]# hadoop fs -ls -R /hdfsapi/test
-rw-r--r-- 3 Administrator supergroup 12 2020-07-01 10:50 /hdfsapi/test/book.txt
-rw-r--r-- 3 Administrator supergroup 11 2020-06-30 20:27 /hdfsapi/test/hello2.txt

之后执行该方法,可以看到控制台输出如下信息:

1
2
3
4
********查看某个目录下的所有文件********
文件 rw-r--r-- 3 12 hdfs://master:9000/hdfsapi/test/book.txt
文件 rw-r--r-- 3 11 hdfs://master:9000/hdfsapi/test/hello2.txt
*********HDFSApp.tearDown()**********

通过对比可以发现/hdfsapi/test目录确实存在上述文件。
(6)查看文件块信息。在HDFSApp类中定义查看文件块信息的方法getFileBlockLocations:

1
2
3
4
5
6
7
8
9
10
11
@Test
public void getFileBlockLocations() throws Exception {
System.out.println("********查看文件块信息********");
FileStatus fileStatus = fileSystem.getFileStatus(new Path("/hdfsapi/test/book.txt"));
BlockLocation[] blockLocations = fileSystem.getFileBlockLocations(fileStatus, 0, fileStatus.getLen());
for(BlockLocation blockLocation:blockLocations){
for(String host:blockLocation.getHosts()){
System.out.println(host);
}
}
}

之后执行该方法,可以看到控制台输出如下信息:

1
2
3
********查看文件块信息********
master
*********HDFSApp.tearDown()**********

这样关于使用Shell和Java API这两种方式来访问HDFS文件系统的学习就到此为止,后续学习其他内容。