首頁 > 軟體

Java操作hdfs檔案系統過程

2022-02-09 10:00:54

1.前置準備

  • 預設伺服器上的hadoop服務已經啟動
  • 本地如果是windows環境,需要本地設定下hadoop的環境變數

本地設定hadoop的環境變數:

2.編碼環境前置準備

使用idea快速構建一個springoot的工程:

1、匯入maven依賴  

  <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>

2、新增一個log4j.properties 檔案

為方便輸出紀錄檔,在springoot工程的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

以上所有的前置準備和程式碼執行環境就準備完畢了,下面就開始具體的API編碼操作hdfs檔案

3.API使用環節

1、建立hdfs檔案目錄

public class HdfsClientTest {

    static Configuration configuration = null;
    static FileSystem fs = null;

    static {

        configuration = new Configuration();
        configuration.set("dfs.client.use.datanode.hostname", "true");
        try {
            fs = FileSystem.get(new URI("hdfs://IP:9000"), configuration, "hadoop");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }

    /**
     * 建立目錄
     */
    public static  void mkDir(String dirName){
        try {
            fs.mkdirs(new Path(dirName));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        //建立檔案目錄
        mkDir("/songguo");
        fs.close();
    }

}    

執行這段程式,然後去web頁面觀察是否建立成功

2、上傳檔案到hdfs檔案目錄

/**
     * 上傳檔案到hdfs
     */
    public static  void uploadFile(String localPath,String hdfsPath){
        try {
            fs.copyFromLocalFile(new Path(localPath), new Path(hdfsPath));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        //建立檔案目錄
        //mkDir("/songguo");
        //上傳檔案到hdfs
        uploadFile("E:\haha.txt", "/songguo");
        fs.close();
    }

3、從hdfs上面下載檔案到本地

/**
     * 從hdfs上面下載檔案到本地
     */
    public static void loadFileFromDfs(String localPath,String hdfsPath){
        try {
            fs.copyToLocalFile(false,new Path(hdfsPath),new Path(localPath),false);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        //建立檔案目錄
        //mkDir("/songguo");

        //上傳檔案到hdfs
        //uploadFile("E:\haha.txt", "/songguo");

        //從hdfs上面下載檔案到本地
        loadFileFromDfs("E:\haha_1.txt","/songguo/haha.txt");

        fs.close();
    }

執行這段程式,觀察E槽下是否成功下載到haha_1.txt檔案

4、刪除hdfs檔案

/**
     * 刪除hdfs檔案
     * @param hdfsPath     檔案路徑
     * @param recuDelete    是否遞迴刪除
     */
    public static void deleteFile(String hdfsPath,boolean recuDelete){
        try {
            fs.delete(new Path(hdfsPath),recuDelete);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        //刪除檔案
        deleteFile("/songguo/haha.txt",false);
        fs.close();
    }

執行程式,觀察web介面的 /songguo目錄下檔案是否被刪除

5、修改hdfs檔名稱

/**
     * 檔案重新命名
     * @param sourceFilePath
     * @param targetFilePath
     */
    public static void renameFile(String sourceFilePath,String targetFilePath){
        try {
            fs.rename(new Path(sourceFilePath),new Path(targetFilePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        //檔案重新命名
        renameFile("/qinguo/haha.txt","/qinguo/haha_rename.txt");
        fs.close();
    }

在 /qinguo 目錄下有一個haha.txt檔案,我們對它進行重新命名操作,執行上面的程式碼

6、移動同時修改hdfs檔名稱

這個和上面的API一樣,仍然使用rename即可,比如將/songuo/haha_rename.txt 移動到 /sanguo目錄下 ,只需要在傳入的引數上面更改即可

//檔案重新命名
renameFile("/qinguo/haha_rename.txt","/sanguo/haha.txt");

上面是目錄下的具體檔案的更名和移動操作,對hdfs檔案目錄同樣適用

7、檔案檢視相關

檢視檔案目錄:

 public static void main(String[] args) throws Exception {
        RemoteIterator<LocatedFileStatus> files  = fs.listFiles(new Path("/"), true);
        while (files.hasNext()){
            //迭代目錄下的具體的檔案資訊
            LocatedFileStatus fileStatus= files.next();
            System.out.println(" ============== 檢視file的相關資訊 ==============");
            System.out.println("檔案路徑 : "+ fileStatus.getPath() );
            System.out.println("檔案路徑名稱:" + fileStatus.getPath().getName());
            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.getReplication());
            System.out.println("檔案塊大小:" + fileStatus.getBlockSize());
        }
        fs.close();
    }

當然,關於檔案資訊,hdfs還有更加豐富的資訊展示,有興趣的同學可以參考官網資料檢視

8、hdfs檔案與資料夾的判斷

從目前對hdfs的瞭解,我們知道檔案和資料夾是有區別的,下面來看如何使用api來進行判斷

public static void main(String[] args) throws Exception {
        FileStatus[] fileStatuses = fs.listStatus(new Path("/"));
        for (FileStatus fileStatus : fileStatuses) {
            boolean directory = fileStatus.isDirectory();
            if(directory){
                System.out.println(fileStatus.getPath().getName() + "是檔案目錄");
            }
            boolean file = fileStatus.isFile();
            if(file){
                System.out.println(fileStatus.getPath().getName() + "是檔案");
            }
        }
        fs.close();
    }

可以看到,在根目錄下有3個目錄,和一個檔案,執行這段程式,看是否能給出正確的判斷呢

通過以上內容,我們基本上了解了如何基於JavaAPI 對hdfs檔案系統的常用操作,也是工作中經常會打交道的內容之一,更多的內容可以在此基礎上繼續深究

4.整合Java 使用者端過程中遇到的幾個坑

事實上,真正在idea中編寫程式碼實現的時候,並非這麼順利,遇到了不少坑,下面分享幾個本次編寫程式碼過程的幾個坑點,希望看到的同學可以合理規避開

1、執行程式直接報無法連線問題

解決方案:

1.在configuration那裡的地址,一定要確認和hdfs裡面的 dataNode的那裡的設定保持一致

2.如果使用的是阿里雲或騰訊雲,那麼在 hdfs-site下面這裡,填上內網地址吧【生產環境下不建議這麼做】,反正我是碰到了

2、上傳檔案情況

上傳檔案到hdfs目錄下,能上傳,但是上傳上去的檔案為空,並且控制檯報錯

報錯資訊如下:

其中比較關鍵的錯誤內容是下面這行:

org.apache.hadoop.ipc.RemoteException(java.io.IOException): File /songguo/haha.txt could only be written to 0 of the 1 minReplication nodes. There are 1 datanode(s) running and 1 node(s) are excluded in this operation.
    at org.apache.hadoop.hdfs.server.blockmanagement.BlockManager.chooseTarget4NewBlock(BlockManager.java:2205)

網上給出了很多解答,但是基本上都是千篇一律,讓重新執行格式化命令,刪除namenode和datanode,然後重啟服務,其實只需要在程式碼中的configuration那裡新增下面一行程式碼設定即可

configuration.set("dfs.client.use.datanode.hostname", "true");

直接給出我們的分析結果:

NameNode節點存放的是檔案目錄,也就是資料夾、檔名稱,本地可以通過公網存取 NameNode,所以可以進行資料夾的建立,當上傳檔案需要寫入資料到DataNode時,NameNode 和DataNode 是通過區域網進行通訊,NameNode返回地址為 DataNode 的私有 IP,本地無法存取

返回的IP地址無法返回公網IP,只能返回主機名,通過主機名與公網地址的對映便可以存取到DataNode節點,問題將解決。

由於程式碼的設定的優先順序為最高,所以直接進行程式碼的設定

到此這篇關於Java操作hdfs檔案系統過程的文章就介紹到這了,更多相關Java操作hdfs檔案系統內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com