API操作
- Shell操作是在叢集內部,即hadoop102上進行操作,API操作是希望在Windows上能遠端連線叢集實現增刪改查操作
一、客戶端環境準備
1、找到資料包路徑下的Windows依賴資料夾,複製hadoop-3.1.0到非中文路徑
2、在Windows上配置HADOOP_HOME環境變數
3、配置Path環境變數
4、驗證Hadoop環境變數是否正常,雙擊winutils.exe,如果報錯,則可能缺少微軟執行庫
5、在IDEA中建立一個Maven工程HdfsClientDemo,並匯入相應的依賴座標+日誌新增
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
</dependencies>
- 在專案的src/main/resources目錄下,新建一個檔案,命名為“log4j.properties”,在檔案中填入:
log4j.rootLogger=INFO, stdout
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/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
6、建立包名:com.user.hdfs
7、建立HdfsClient類
package com.user.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
public class HdfsClient {
private FileSystem fs;
@Before
public void init() throws URISyntaxException, IOException, InterruptedException {
// 1 獲取檔案系統
//連線的叢集地址
URI uri = new URI("hdfs://hadoop102:8020");
//建立一個配置檔案
Configuration configuration = new Configuration();
//使用者
String user = "user";
//獲取客戶端物件
this.fs = FileSystem.get(uri, configuration, user);
}
@After
public void close() throws IOException {
// 3 關閉資源
fs.close();
}
@Test
public void testmkdirs() throws IOException, URISyntaxException, InterruptedException {
// 2 建立目錄
fs.mkdirs(new Path("/xiyou/huaguoshan1"));
}
}
注意
- 在以上程式碼中,獲取客戶端物件這麼寫
FileSystem fs = FileSystem.get(uri, configuration, user);
會丟擲丟擲NullPointerException,因為在@Before註解的init方法中,我們在方法內部建立了FileSystem的一個新例項,但是沒有將其賦值給類的成員變數fs。由於成員變數fs沒有被初始化,它仍然是null,所以在@After註解的close方法中呼叫fs.close()時會丟擲NullPointerException - 在HDFS Web端刪除檔案huaguoshan1報錯
Permission denied: user=dr.who, access=WRITE, inode="/xiyou":user:supergroup:drwxr-xr-x
表示使用者dr.who試圖對/xiyou目錄進行寫操作,但是沒有足夠的許可權,只好在hadoop102使用Shell命令刪除檔案
二、其他API案例實操
1、從本地上傳檔案
- 首先在本地D盤建立一個文字檔案sunwukong.txt
- 在上述HdfsClient類程式碼裡繼續新增如下程式碼,以下例項也一樣
// 上傳
@Test
public void testPut() throws IOException {
//引數解讀
//引數一:表示是否刪除原資料
//引數二:表示是否允許覆蓋
//引數三:原資料路徑
//引數四:目的地路徑
fs.copyFromLocalFile(false,false,new Path("D:\\sunwukong.txt"),new Path("/xiyou/huaguoshan"));
}
注意引數優先順序
- 引數優先順序從低到高:hdfs-default.xml —> hdfs-site.xml —> 在專案資源目錄下的配置檔案 —> 程式碼裡面的配置
- 在上面的檔案上傳的程式碼中,沒有額外設定副本數,即Replication的值為3
- 假設我們在專案資源目錄下新建檔案hdfs-site.xml並新增如下內容,那麼上傳檔案後顯示的Replication值為1
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
</configuration>
- 假設我們繼續在init方法中新增如下內容,則上傳檔案後的Replication值為2
configuration.set("dfs.replication", "2");
2、從HDFS下載檔案到本地
// 檔案下載 從HDFS下載資料到本地
@Test
public void testGet() throws IOException {
//引數一:是否刪除原檔案
//引數二:原檔案的路徑(HDFS)
//引數三:目標地址路徑(Win)
//引數四:是否開啟本地校驗,true是沒有開啟校驗,最後下載的檔案裡沒有.crc檔案
fs.copyToLocalFile(false,new Path("/xiyou/huaguoshan/sunwukong.txt"),new Path("C:\\sunwukong1.txt"),false);
}
3、檔案刪除
// 刪除
@Test
public void testRm() throws IOException {
//引數一:要刪除的路徑
//引數二:是否要遞迴刪除
//fs.delete(new Path("/jdk-8u212-linux-x64.tar.gz"),false);
//刪除非空目錄
fs.delete(new Path("/xiyou"),true);
}
4、檔案更名和移動
// 檔案的更名和移動
@Test
public void testmv() throws IOException {
//檔名稱修改
//引數一:原檔案路徑
//引數二:目標檔案路徑
//fs.rename(new Path("/input/word.txt"),new Path("/input/SEVENTEEN.txt"));
//檔案的移動和更名
fs.rename(new Path("/input/SEVENTEEN.txt"),new Path("/CREW.txt"));
//目錄的更名
fs.rename(new Path("/input"),new Path("/result"));
}
5、檔案詳情檢視
// 獲取檔案詳細資訊
@Test
public void fileDetail() throws IOException {
// 獲取所有檔案資訊 這是一個迭代器
RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);
//遍歷檔案
while (listFiles.hasNext()) {
LocatedFileStatus fileStatus = listFiles.next();
System.out.println("===========" + fileStatus.getPath() + "===========");
System.out.println(fileStatus.getPermission());
System.out.println(fileStatus.getOwner());
System.out.println(fileStatus.getGroup());
System.out.println(fileStatus.getLen());
System.out.println(fileStatus.getModificationTime());
System.out.println(fileStatus.getReplication());
System.out.println(fileStatus.getBlockSize());
System.out.println(fileStatus.getPath().getName());
// 獲取塊資訊
BlockLocation[] blockLocations = fileStatus.getBlockLocations();
System.out.println(Arrays.toString(blockLocations));
}
}
6、檔案和資料夾的判斷
// 檔案和資料夾判斷
@Test
public void testFile() throws IOException {
FileStatus[] listStatus = fs.listStatus(new Path("/"));
for (FileStatus status : listStatus) {
if (status.isFile()) {
System.out.println("檔案:" + status.getPath().getName());
}else {
System.out.println("目錄:" + status.getPath().getName());
}
}
}