0 - 配置 Hadoop 環境(Windows系統)
下述步驟適用於 Windows 系統,其他系統可忽略。
在 Windows 系統直接運行 Hadoop 相關代碼,會提示缺少 winutils.exe
和 hadoop.dll
文件:
java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries.
WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
原因:通過代碼訪問 Hadoop 集羣,本地開發環境相當於 Hadoop 客戶端,需要有 Hadoop 相關軟件纔可正常運行。
配置步驟:
1)到 https://github.com/cdarlint/winutils 下載與集羣版本相匹配的文件夾,然後將此文件夾拷貝到沒有中文和空格的路徑下,比如 D:\software\hadoop-3.2.1
;
2)在 Windows 的環境變量中添加 HADOOP_HOME
,值爲上面的路徑,並將 %HADOOP_HOME%\bin
添加到 path
中;
3)把上述文件夾 bin目錄下的 hadoop.dll
文件拷貝到系統盤 C:\Windows\System32
目錄;
4)重啓 Windows 電腦。
1 - 導入 Maven 依賴
鑑於篇幅有限,相關 Maven 依賴請參見:《https://github.com/healchow/bigdata-study/blob/main/pom.xml》
2 - 常用類介紹
通過 Java API 操作 HDFS,主要涉及以下 class:
1)Configuration
主要用來封裝客戶端 / 服務端的配置。
2)FileSystem
這個類的對象是一個文件系統對象,可以用該對象的一些方法來對文件進行操作。
可通過靜態方法獲得該對象:
// 通過 conf 中的 “fs.defaultFS” 參數的值來確定文件系統的具體類型
FileSystem fs = FileSystem.get(conf);
如果代碼中沒有指定 fs.defaultFS
,並且工程的 ClassPath 下也沒有相應的配置,此參數的默認值就由 Hadoop Jar 包中的 core-default.xml
文件來確定:
默認值是 file:///
,獲取的不是 DistributedFileSystem 的實例,而是一個本地文件系統的客戶端對象。
3 - 常見 API 操作
3.1 獲取文件系統(重要)
方式1:FileSystem.get(conf)
/**
* 獲取 FileSystem - FileSystem.get()
*/
@Test
public void testGetFileSystem1() throws IOException {
// 創建 Configuration 對象
Configuration conf = new Configuration();
// 指定文件系統類型
conf.set("fs.defaultFS", "hdfs://hadoop:9000");
// 獲取指定的文件系統
FileSystem fileSystem = FileSystem.get(conf);
// FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop:9000"), new Configuration());
// 結果:DFS[DFSClient[clientName=DFSClient_NONMAPREDUCE_1219793882_1, ugi=healchow (auth:SIMPLE)]]
System.out.println(fileSystem);
// 關閉文件系統
fileSystem.close();
}
方式2:FileSystem.newInstance(conf)
/**
* 獲取 FileSystem - FileSystem.newInstance()
*/
@Test
public void testGetFileSystem2() throws IOException {
// 創建 Configuration 對象
Configuration conf = new Configuration();
// 指定文件系統類型
conf.set("fs.defaultFS", "hdfs://hadoop:9000");
// 獲取指定的文件系統
FileSystem fileSystem = FileSystem.newInstance(conf);
// FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://hadoop:9000"), new Configuration());
System.out.println(fileSystem);
fileSystem.close();
}
3.2 創建目錄、寫入文件
/**
* 通過 HDFS URL 創建目錄、寫入文件
*/
@Test
public void testPutFile() throws IOException, URISyntaxException {
// 創建測試目錄(可創建多級目錄)
FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://hadoop:9000"), new Configuration());
boolean result = fileSystem.mkdirs(new Path("/test/input"));
System.out.println("mkdir result: " + result);
// 創建文件,若存在則覆蓋,返回的是寫入文件的輸出流
FSDataOutputStream outputStream = fileSystem.create(new Path("/test/input/hello.txt"), true);
String content = "hello,hadoop\nhello,hdfs";
outputStream.write(content.getBytes(StandardCharsets.UTF_8));
// 關閉流(不拋出異常)
IOUtils.closeQuietly(outputStream);
}
3.3 上傳文件
/**
* 向 HDFS 上傳文件 - copyFromLocalFile()
*/
@Test
public void testUploadFile() throws URISyntaxException, IOException {
// 獲取 FileSystem
FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop:9000"), new Configuration());
// 從本地上傳文件,兩個參數都要指定到具體的文件
fileSystem.copyFromLocalFile(new Path("/Users/healchow/bigdata/core-site.xml"),
new Path("/test/upload/core-site.xml"));
// 關閉FileSystem
fileSystem.close();
}
3.4 下載文件
HDFS URL 打開 InputStream 的方式:
/**
* 通過 HDFS URL 獲取文件並下載 - IOUtils.copy() 方法
*/
@Test
public void testDownFileByUrl() throws IOException {
// 註冊 HDFS URL
URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
// 獲取 HDFS 文件的輸入流
InputStream inputStream = new URL("hdfs://hadoop:9000/test/input/hello.txt").openStream();
// 獲取本地文件的輸出流(絕對路徑,文件夾必須存在)
FileOutputStream outputStream = new FileOutputStream("/Users/healchow/bigdata/test/hello.txt");
// 拷貝文件
IOUtils.copy(inputStream, outputStream);
// 關閉流(不拋出異常)
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
}
FileSystem 打開 InputStream 的方式:
/**
* 通過 FileSystem 獲取文件並下載 - IOUtils.copy() 方法
*/
@Test
public void testDownloadFile() throws URISyntaxException, IOException {
// 獲取 FileSystem
FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop:9000"), new Configuration());
// 獲取 HDFS 文件的輸入流
FSDataInputStream inputStream = fileSystem.open(new Path("/test/input/hello.txt"));
// 獲取本地文件的輸出流
FileOutputStream outputStream = new FileOutputStream("/Users/healchow/bigdata/test/hello1.txt");
// 拷貝文件
IOUtils.copy(inputStream, outputStream);
// 關閉流
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
fileSystem.close();
}
FileSystem#copyToLocalFile() 的方式:
/**
* 通過 FileSystem 獲取文件並下載 - copyToLocalFile() 方法
*/
@Test
public void testDownloadFileByCopyTo() throws URISyntaxException, IOException, InterruptedException {
// 獲取 FileSystem
FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop:9000"), new Configuration(), "root");
// copyToLocalFile 拷貝文件到本地,會下載 CRC 校驗文件
fileSystem.copyToLocalFile(new Path("/test/input/hello.txt"),
new Path("/Users/healchow/bigdata/test/hello2.txt"));
// 關閉 FileSystem
fileSystem.close();
}
3.5 遍歷 HDFS 的文件
/**
* 遍歷 HDFS 文件
*/
@Test
public void testListFiles() throws URISyntaxException, IOException {
// 獲取FileSystem實例
FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop:9000"), new Configuration());
// 遞歸獲取 /test 目錄下所有的文件信息
RemoteIterator<LocatedFileStatus> iterator = fileSystem.listFiles(new Path("/test"), true);
// 遍歷文件
while (iterator.hasNext()) {
LocatedFileStatus fileStatus = iterator.next();
// 獲取文件的絕對路徑:hdfs://hadoop:9000/xxx
System.out.println("filePath: " + fileStatus.getPath());
// 文件的 block 信息
BlockLocation[] blockLocations = fileStatus.getBlockLocations();
for (BlockLocation blockLocation : blockLocations) {
String[] hosts = blockLocation.getHosts();
for (String host : hosts) {
System.out.println("blockHost: " + host);
}
}
System.out.println("blockSize: " + blockLocations.length);
}
}
4 - HDFS 的訪問權限控制
從上面的 API 練習,不難發現:只要得到了 HDFS 的 URL(即 fs.defaultFS
)配置項,能訪問到集羣的任何人都能讀寫 HDFS 上的數據,這會導致數據的安全性完全無法得到保障。
爲了解決這個問題,HDFS 有 訪問權限控制的方法,只有通過認證的用戶,按照其所擁有的權限,讀取或寫入某些目錄下的文件。
開啓 HDFS 訪問權限控制的方法如下:
1)停止 HDFS 集羣:
cd ~/bigdata/hadoop-3.2.1
sbin/stop-dfs.sh
2)修改 ~/bigdata/hadoop-3.2.1/etc/hadoop/hdfs-site.xml
中的配置,添加如下內容:
<property>
<name>dfs.permissions.enabled</name>
<value>true</value>
</property>
4)重啓 HDFS 集羣:
cd ~/bigdata/hadoop-3.2.1
sbin/start-dfs.sh
5)上傳測試文件到 HDFS 集羣,這裏將上傳後的一個文件的權限修改爲 600
,即只能所有者讀寫:
cd ~/bigdata/hadoop-3.2.1/etc/hadoop
hdfs dfs -mkdir /test/config
hdfs dfs -put *.xml /test/config
hdfs dfs -chmod 600 /test/config/core-site.xml
6)通過代碼下載文件:
/**
* 通過下載文件,測試訪問權限控制
*/
@Test
public void testAccessControl() throws Exception {
// 開啓權限控制後,當前用戶(啓動 NameNode 的用戶)應當能成功訪問
// FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop:9000"), new Configuration());
// 僞造其他用戶訪問,應當訪問失敗
FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop:9000"), new Configuration(), "testuser");
fileSystem.copyToLocalFile(new Path("/test/config/core-site.xml"),
new Path("file:/Users/healchow/bigdata/core-site.xml"));
fileSystem.close();
}
說明:本地測試失敗。無論用哪個用戶,訪問都成功。
查了很多資料,沒有說得通的。勞煩有了解的大佬,留言告知我呀🙏
版權聲明
出處:博客園-瘦風的南牆(https://www.cnblogs.com/shoufeng)
感謝閱讀,公衆號 「瘦風的南牆」 ,手機端閱讀更佳,還有其他福利和心得輸出,歡迎掃碼關注🤝
本文版權歸博主所有,歡迎轉載,但 [必須在頁面明顯位置標明原文鏈接],否則博主保留追究相關人士法律責任的權利。