大數據學習系列:Hadoop3.0苦命學習(二)


HDFS(Hadoop Distributed File System) 是一個 Apache Software Foundation 項目,是 Apache Hadoop 項目的一個子項目.。Hadoop 非常適於存儲大型數據 (比如 TB 和 PB), 其就是使用 HDFS 作爲存儲系統.。HDFS 使用多臺計算機存儲文件, 並且提供統一的訪問接口,像是訪問一個普通文件系統一樣使用分佈式文件系統。 HDFS 對數據文件的訪問通過流的方式進行處理,這意味着通過命令和 MapReduce 程序的方式可以直接使用 HDFS。HDFS 是容錯的,且提供對大數據集的高吞吐量訪問。

HDFS 的一個非常重要的特點就是一次寫入、多次讀取, 該模型降低了對併發控制的要求, 簡化了數據聚合性, 支持高吞吐量訪問. 而吞吐量是大數據系統的一個非常重要的指標, 吞吐量高意味着能處理的數據量就大.

1 設計目標

  • 分佈式:通過跨多個廉價計算機集羣分佈數據和處理來節約成本
  • 副本機制:通過自動維護多個數據副本和在故障發生時來實現可靠性
  • 易擴展:它們爲存儲和處理超大規模數據提供所需的擴展能力。

2 HDFS 的歷史

  1. Doug Cutting 在做 Lucene 的時候, 需要編寫一個爬蟲服務, 這個爬蟲寫的並不順利, 遇到了一些問題, 諸如: 如何存儲大規模的數據, 如何保證集羣的可伸縮性, 如何動態容錯等
  2. 2013年的時候, Google 發佈了三篇論文, 被稱作爲三駕馬車, 其中有一篇叫做 GFS, 是描述了 Google 內部的一個叫做 GFS 的分佈式大規模文件系統, 具有強大的可伸縮性和容錯性
  3. Doug Cutting 後來根據 GFS 的論文, 創造了一個新的文件系統, 叫做 HDFS

3 HDFS 的架構

在這裏插入圖片描述

  1. NameNode 是一箇中心服務器, 單一節點(簡化系統的設計和實現), 負責管理文件系統的名字空間(NameSpace)以及客戶端對文件的訪問
  2. 文件操作, NameNode 是負責文件元數據的操作, DataNode 負責處理文件內容的讀寫請求, 跟文件內容相關的數據流不經過 NameNode, 只詢問它跟哪個 DataNode聯繫, 否則 NameNode 會成爲系統的瓶頸
  3. 副本存放在哪些 DataNode 上由 NameNode 來控制, 根據全局情況作出塊放置決定, 讀取文件時 NameNode 儘量讓用戶先讀取最近的副本, 降低讀取網絡開銷和讀取延時
  4. NameNode 全權管理數據庫的複製, 它週期性的從集羣中的每個 DataNode 接收心跳信合和狀態報告, 接收到心跳信號意味着 DataNode 節點工作正常, 塊狀態報告包含了一個該 DataNode 上所有的數據列表
NameNode DataNode
存儲元數據 存儲文件內容
元數據保存在內存中 文件內容保存在磁盤
保存文件, block, DataNode 之間的關係 維護了 block id 到 DataNode 文件之間的關係

4 HDFS 文件副本和 Block 塊存儲

在這裏插入圖片描述

所有的文件都是以 block 塊的方式存放在 HDFS 文件系統當中, 在 Hadoop1 當中, 文件的 block 塊默認大小是 64M, hadoop2 當中, 文件的 block 塊大小默認是 128M, block 塊的大小可以通過 hdfs-site.xml 當中的配置文件進行指定

<property>
    <name>dfs.block.size</name>
    <value>塊大小 以字節爲單位</value>
</property>

4.1 引入塊機制的好處

  1. 一個文件有可能大於集羣中任意一個磁盤
  2. 使用塊抽象而不是文件可以簡化存儲子系統
  3. 塊非常適合用於數據備份進而提供數據容錯能力和可用性

4.2 塊緩存

通常 DataNode 從磁盤中讀取塊, 但對於訪問頻繁的文件, 其對應的塊可能被顯式的緩存在 DataNode 的內存中, 以堆外塊緩存的形式存在. 默認情況下,一個塊僅緩存在一個 DataNode 的內存中,當然可以針對每個文件配置 DataNode 的數量. 作業調度器通過在緩存塊的 DataNode 上運行任務, 可以利用塊緩存的優勢提高讀操作的性能.

例如:連接(join) 操作中使用的一個小的查詢表就是塊緩存的一個很好的候選

用戶或應用通過在緩存池中增加一個 Cache Directive 來告訴 NameNode 需要緩存哪些文件及存多久. 緩存池(Cache Pool) 是一個擁有管理緩存權限和資源使用的管理性分組.

例如:一個文件 130M, 會被切分成 2 個 block 塊, 保存在兩個 block 塊裏面, 實際佔用磁盤 130M 空間, 而不是佔用256M的磁盤空間

4.3 HDFS 文件權限驗證

HDFS 的文件權限機制與 Linux 系統的文件權限機制類似

r:read  w:write  x:execute

權限 x 對於文件表示忽略, 對於文件夾表示是否有權限訪問其內容 如果 Linux 系統用戶 zhangsan 使用 Hadoop 命令創建一個文件, 那麼這個文件在 HDFS 當中的 Owner 就是 zhangsan HDFS 文件權限的目的, 防止好人做錯事, 而不是阻止壞人做壞事. HDFS相信你告訴我你是誰, 你就是誰

5 HDFS 的元信息和 SecondaryNameNode

當 Hadoop 的集羣當中, 只有一個 NameNode 的時候, 所有的元數據信息都保存在了 FsImage 與 Eidts 文件當中, 這兩個文件就記錄了所有的數據的元數據信息, 元數據信息的保存目錄配置在了 hdfs-site.xml 當中

<property>
  <name>dfs.namenode.name.dir</name>
  <value>file:///export/services/hadoop-3.1.1/datas/namenode/namenodedatas</value>
</property>
<property>
  <name>dfs.namenode.edits.dir</name>
  <value>file:///export/services/hadoop-3.1.1/datas/dfs/nn/edits</value>
</property>

5.1 FsImage 和 Edits 詳解

  • edits
    • edits 存放了客戶端最近一段時間的操作日誌
    • 客戶端對 HDFS 進行寫文件時會首先被記錄在 edits 文件中
    • edits 修改時元數據也會更新
    • 每次 HDFS 更新時 edits 先更新後客戶端纔會看到最新信息
  • fsimage
    • NameNode 中關於元數據的鏡像, 一般稱爲檢查點, fsimage 存放了一份比較完整的元數據信息
    • 因爲 fsimage 是 NameNode 的完整的鏡像, 如果每次都加載到內存生成樹狀拓撲結構,這是非常耗內存和CPU, 所以一般開始時對 NameNode 的操作都放在 edits 中
    • fsimage 內容包含了 NameNode 管理下的所有 DataNode 文件及文件 block 及 block 所在的 DataNode 的元數據信息.
    • 隨着 edits 內容增大, 就需要在一定時間點和 fsimage 合併

5.2 fsimage 中的文件信息查看

官方查看文檔

使用命令 hdfs oiv

cd /export/services/hadoop-3.1.1/datas/namenode/namenodedatas
hdfs oiv -i fsimage_0000000000000000864 -p XML -o hello.xml

在這裏插入圖片描述

5.3 edits 中的文件信息查看

官方查看文檔

使用命令 hdfs oev

cd /export/services/hadoop-3.1.1/datas/dfs/nn/edits
hdfs oev -i  edits_0000000000000000865-0000000000000000866 -o myedit.xml -p XML

在這裏插入圖片描述

5.4 SecondaryNameNode 如何輔助管理 fsimage 與 edits 文件?

  • SecondaryNameNode 定期合併 fsimage 和 edits, 把 edits 控制在一個範圍內

  • 配置 SecondaryNameNode

    • SecondaryNameNode 在 conf/masters 中指定

    • 在 masters 指定的機器上, 修改 hdfs-site.xml

      <property>
      	<name>dfs.http.address</name>
      	<value>host:50070</value>
      </property>
      
    • 修改 core-site.xml, 這一步不做配置保持默認也可以

      <!-- 多久記錄一次 HDFS 鏡像, 默認 1小時 -->
      <property>
      	<name>fs.checkpoint.period</name>
          <value>3600</value>
      </property>
      
      <!-- 一次記錄多大, 默認 64M -->
      <property>
          <name>fs.checkpoint.size</name>
          <value>67108864</value>
      </property>
      

在這裏插入圖片描述

  1. SecondaryNameNode 通知 NameNode 切換 editlog
  2. SecondaryNameNode 從 NameNode 中獲得 fsimage 和 editlog(通過http方式)
  3. SecondaryNameNode 將 fsimage 載入內存, 然後開始合併 editlog, 合併之後成爲新的 fsimage
  4. SecondaryNameNode 將新的 fsimage 發回給 NameNode
  5. NameNode 用新的 fsimage 替換舊的 fsimage

特點

  • 完成合並的是 SecondaryNameNode, 會請求 NameNode 停止使用 edits, 暫時將新寫操作放入一個新的文件中 edits.new
  • SecondaryNameNode 從 NameNode 中通過 Http GET 獲得 edits, 因爲要和 fsimage 合併, 所以也是通過 Http Get 的方式把 fsimage 加載到內存, 然後逐一執行具體對文件系統的操作, 與 fsimage 合併, 生成新的 fsimage, 然後通過 Http POST 的方式把 fsimage 發送給 NameNode. NameNode 從 SecondaryNameNode 獲得了 fsimage 後會把原有的 fsimage 替換爲新的 fsimage, 把 edits.new 變成 edits. 同時會更新 fstime
  • Hadoop 進入安全模式時需要管理員使用 dfsadmin 的 save namespace 來創建新的檢查點
  • SecondaryNameNode 在合併 edits 和 fsimage 時需要消耗的內存和 NameNode 差不多, 所以一般把 NameNode 和 SecondaryNameNode 放在不同的機器上

6 HDFS 文件寫入過程

在這裏插入圖片描述

  1. Client 發起文件上傳請求,通過 RPC 與 NameNode 建立通訊,NameNode 檢查目標文件是否已存在, 父目錄是否存在, 返回是否可以上傳
  2. Client 請求第一個 block 該傳輸到哪些 DataNode 服務器上
  3. NameNode 根據配置文件中指定的備份數量及機架感知原理進行文件分配, 返回可用的 DataNode 的地址如: A, B, C
    • Hadoop 在設計時考慮到數據的安全與高效, 數據文件默認在 HDFS 上存放三份, 存儲策略爲本地一份, 同機架內其它某一節點上一份, 不同機架的某一節點上一份。
  4. Client 請求 3 臺 DataNode 中的一臺 A 上傳數據(本質上是一個 RPC 調用,建立 pipeline ), A 收到請求會繼續調用 B, 然後 B 調用 C, 將整個 pipeline 建立完成, 後逐級返回 client
  5. Client 開始往 A 上傳第一個 block(先從磁盤讀取數據放到一個本地內存緩存), 以 packet 爲單位(默認64K), A 收到一個 packet 就會傳給 B, B 傳給 C. A 每傳一個 packet 會放入一個應答隊列等待應答
  6. 數據被分割成一個個 packet 數據包在 pipeline 上依次傳輸, 在 pipeline 反方向上, 逐個發送 ack(命令正確應答), 最終由 pipeline 中第一個 DataNode 節點 A 將 pipelineack 發送給 Client
  7. 當一個 block 傳輸完成之後, Client 再次請求 NameNode 上傳第二個 block 到服務 1

7 HDFS 文件讀取過程

  1. Client向NameNode發起RPC請求,來確定請求文件block所在的位置;
  2. NameNode會視情況返回文件的部分或者全部block列表,對於每個block,NameNode 都會返回含有該 block 副本的 DataNode 地址; 這些返回的 DN 地址,會按照集羣拓撲結構得出 DataNode 與客戶端的距離,然後進行排序,排序兩個規則:網絡拓撲結構中距離 Client 近的排靠前;心跳機制中超時彙報的 DN 狀態爲 STALE,這樣的排靠後;
  3. Client 選取排序靠前的 DataNode 來讀取 block,如果客戶端本身就是DataNode,那麼將從本地直接獲取數據(短路讀取特性);
  4. 底層上本質是建立 Socket Stream(FSDataInputStream),重複的調用父類 DataInputStream 的 read 方法,直到這個塊上的數據讀取完畢;
  5. 當讀完列表的 block 後,若文件讀取還沒有結束,客戶端會繼續向NameNode 獲取下一批的 block 列表;
  6. 讀取完一個 block 都會進行 checksum 驗證,如果讀取 DataNode 時出現錯誤,客戶端會通知 NameNode,然後再從下一個擁有該 block 副本的DataNode 繼續讀。
  7. read 方法是並行的讀取 block 信息,不是一塊一塊的讀取;NameNode 只是返回Client請求包含塊的DataNode地址,並不是返回請求塊的數據;
  8. 最終讀取來所有的 block 會合併成一個完整的最終文件。

8 HDFS的命令行使用

語法:hdfs dfs 參數

hdfs 常用的操作命令
hdfs dfs -ls /    // 查看根路徑下的文件或文件夾
hdfs dfs -mkdir -p /xx/xxx    //在hdfs上遞歸創建文件夾
hdfs dfs -moveFromLocal sourceDir(本地磁盤的文件或文件夾路徑) destDir(hdfs的路徑)
hdfs dfs -mv hdfsSourceDir hdfsDestDir
hdfs dfs -put localDir hdfsDir    //將本地文件系統的文件或文件夾放到hdfs上去
hdfs dfs -appendToFile a.txt b.txt /hello.txt
hdfs dfs -cat hdfsDir     //查看hdfs的文件內容
hdfs dfs -cp hdfsSourceDir hdfsDestDir    //拷貝文件或者文件夾
hdfs dfs -rm [-r]    //(遞歸)刪除文件或者文件夾

hdfs的權限管理兩個命令:
hdfs dfs -chmod -R 777 /xxx
hdfs dfs -chown -R hadoop:hadoop /xxx

9 HDFS 的 API 操作

在windows系統配置hadoop運行環境:
第一步:將apache-hadoop-3.1.1文件拷貝到一個沒有中文沒有空格的路徑下面
第二步:在windows上面配置hadoop的環境變量:HADOOP_HOME;以及將其bin目錄加入path中
第三步:將bin目錄中的hadoop.dll文件放到系統盤 C:\Windows\System32
第四步:重啓Windows

9.1 導入 Maven 依賴

<repositories>
    <repository>
        <id>cloudera</id>
        <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
    </repository>
</repositories>
<dependencies>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>3.1.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>3.1.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs-client</artifactId>
        <version>3.1.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-client</artifactId>
        <version>3.1.1</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.9</version>
    </dependency>

</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>2.4.3</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <minimizeJar>true</minimizeJar>
                    </configuration>
                </execution>
            </executions>
        </plugin>

    </plugins>
</build>

9.2 概述

在 Java 中操作 HDFS, 主要涉及以下 Class:

  • Configuration

    • 該類的對象封轉了客戶端或者服務器的配置
  • FileSystem

    • 該類的對象是一個文件系統對象, 可以用該對象的一些方法來對文件進行操作, 通過 FileSystem 的靜態方法 get 獲得該對象

      FileSystem fs = FileSystem.get(conf)
      
      • get 方法從 conf 中的一個參數 fs.defaultFS 的配置值判斷具體是什麼類型的文件系統
      • 如果我們的代碼中沒有指定 fs.defaultFS, 並且工程 ClassPath 下也沒有給定相應的配置, conf 中的默認值就來自於 Hadoop 的 Jar 包中的 core-default.xml
      • 默認值爲 file:///, 則獲取的不是一個 DistributedFileSystem 的實例, 而是一個本地文件系統的客戶端對象

在這裏插入圖片描述

9.3 獲取 FileSystem 的幾種方式

備註:要在下面代碼中直接使用node**代替192.168.188.***需要設置的Window中System32中的hosts文件進行映射,否則直接使用192.168.188.***代替即可。

  • 第一種方式
@Test
public void getFileSystem() throws IOException {
    // 獲取Configuration對象
    Configuration configuration = new Configuration();
    // 設置Configuration對象,設置的目的是來指定要操作的文件系統
    configuration.set("fs.defaultFS", "hdfs://node01:8020");

    // 獲取指定的文件系統,獲取FileSystem就相當於獲取了主節點中所有的元數據信息
    FileSystem fileSystem = FileSystem.get(configuration);
    System.out.println("fileSystem: " + fileSystem.toString());
}

輸出結果:
在這裏插入圖片描述

  • 第二種方式
@Test
public void getFileSystem2() throws IOException, URISyntaxException {
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
    System.out.println(fileSystem);
}

執行結果:
在這裏插入圖片描述

  • 第三種方式
@Test
public void getFileSystem3() throws IOException, URISyntaxException {
    Configuration configuration = new Configuration();
    configuration.set("fs.defaultFS", "hdfs://node01:8020");

    FileSystem fileSystem = FileSystem.newInstance(configuration);
    System.out.println("fileSystem: " + fileSystem.toString());
}

執行結果:
在這裏插入圖片描述

  • 第四種方式
@Test
public void getFileSystem4() throws IOException, URISyntaxException {
    FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://node01:8020"), new Configuration());
    System.out.println(fileSystem);
}

在這裏插入圖片描述

9.4 遍歷 HDFS 中所有文件

  • 遞歸遍歷
@Test
public void listFile() throws Exception{
    // 1. 獲取FileSystem
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
    // 2. 獲取指定目錄下的所有信息,第一個參數指定遍歷的路徑,第二個參數表示是否要
    RemoteIterator<LocatedFileStatus> iterator = fileSystem.listFiles(new Path("/"), true);
    // 3. 遍歷迭代器
    while (iterator.hasNext()){
        // 獲取每一個文件的詳細信息
        LocatedFileStatus fileStatus = iterator.next();
        // 獲取每一個文件的存儲路徑
        System.out.println(fileStatus.getPath()+"---"+fileStatus.getPath().getName());
        // 獲取文件的block存儲信息
        BlockLocation[] blockLocations = fileStatus.getBlockLocations();
        // 打印每一個文件的block數
        System.out.println(blockLocations.length);
        // 打印每一個block副本的儲存位置
        for (BlockLocation blockLocation : blockLocations) {
            String[] hosts = blockLocation.getHosts();
            for (String host : hosts) {
                System.out.print(host+" <-> ");
            }
            System.out.println();
        }
    }

}

執行結果:
在這裏插入圖片描述

9.5 下載文件到本地

@Test
public void downLoadFileTest() throws URISyntaxException, IOException {
    // 1. 獲取FileSystem對象
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
    // 2. 獲取Hdfs文件輸入流
    FSDataInputStream inputStream = fileSystem.open(new Path("/aaa/zookeeper.out"));
    // 3. 獲取本地文件的輸出流
    FileOutputStream outputStream = new FileOutputStream(new File("F://zookeeper.out"));
    // 4. 實現文件的複製
    IOUtils.copy(inputStream, outputStream);
    // 5. 關閉流
    IOUtils.closeQuietly(inputStream);
    IOUtils.closeQuietly(outputStream);
    fileSystem.close();
}

更簡單的下載方式:

@Test
public void downLoadFileTest2() throws URISyntaxException, IOException {
    // 1. 獲取FileSystem對象
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
    // 2. 調用方法直接實現文件的下載
    fileSystem.copyToLocalFile(new Path("/aaa/zookeeper.out"), new Path("E://zookeeper.out"));
    fileSystem.close();
}

9.6 HDFS 上創建文件夾和文件

@Test
public void mkdirsTest() throws URISyntaxException, IOException {
    // 獲取
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
    // 創建文件夾
    fileSystem.mkdirs(new Path("/app/text/hello"));

    // 創建文件
    fileSystem.create(new Path("/a.txt"));

}

查詢執行的結果:
在這裏插入圖片描述

9.7 HDFS 文件上傳

@Test
public void uploadFileTest() throws IOException, URISyntaxException {
    // 1. 獲取FileSystem對象
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
    // 2. 調用方法實現文件的上傳
    fileSystem.copyFromLocalFile(new Path("F://hello.txt"), new Path("/hello.txt"));
    fileSystem.close();
}

9.8 僞造用戶

  1. 停止hdfs集羣,在node01機器上執行以下命令
/export/services/hadoop-3.1.1/sbin/stop-yarn.sh
/export/services/hadoop-3.1.1/sbin/stop-dfs.sh
  1. 修改node01機器上的hdfs-site.xml當中的配置文件
cd /export/services/hadoop-3.1.1/etc/hadoop
vim hdfs-site.xml
<property>
    <name>dfs.permissions.enabled</name>
    <value>true</value>
</property>
  1. 修改完成之後配置文件發送到其他機器上面去
scp hdfs-site.xml node02:$PWD
scp hdfs-site.xml node03:$PWD
  1. 重啓hdfs集羣
cd /export/services/hadoop-3.1.1
sbin/start-dfs.sh
  1. 隨意上傳一些文件到我們hadoop集羣當中準備測試使用
cd /export/services/hadoop-3.1.1/etc/hadoop
hdfs dfs -mkdir /config
hdfs dfs -put *.xml /config
hdfs dfs -chmod 600 /config/core-site.xml
  1. 使用代碼準備下載文件
@Test
public void getConfig()throws  Exception{
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.188.100:8020"), new Configuration(),"root");
    fileSystem.copyToLocalFile(new Path("/config/core-site.xml"),new Path("file:///c:/core-site.xml"));
    fileSystem.close();
}

9.9 小文件合併

由於 Hadoop 擅長存儲大文件,因爲大文件的元數據信息比較少,如果 Hadoop 集羣當中有大量的小文件,那麼每個小文件都需要維護一份元數據信息,會大大的增加集羣管理元數據的內存壓力,所以在實際工作當中,如果有必要一定要將小文件合併成大文件進行一起處理

在我們的 HDFS 的 Shell 命令模式下,可以通過命令行將很多的 hdfs 文件合併成一個大文件下載到本地

cd /export/servers
hdfs dfs -getmerge /config/*.xml ./hello.xml

既然可以在下載的時候將這些小文件合併成一個大文件一起下載,那麼肯定就可以在上傳的時候將小文件合併到一個大文件裏面去

在這裏插入圖片描述

@Test
public void mergeFile() throws  Exception{
    //獲取分佈式文件系統
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.250:8020"), new Configuration(),"hadoop");
    FSDataOutputStream outputStream = fileSystem.create(new Path("/bigfile.xml"));
    //獲取本地文件系統
    LocalFileSystem local = FileSystem.getLocal(new Configuration());
    //通過本地文件系統獲取文件列表,爲一個集合
    FileStatus[] fileStatuses = local.listStatus(new Path("F:\\input"));
    for (FileStatus fileStatus : fileStatuses) {
        FSDataInputStream inputStream = local.open(fileStatus.getPath());
        IOUtils.copy(inputStream,outputStream);
        IOUtils.closeQuietly(inputStream);
    }
    IOUtils.closeQuietly(outputStream);
    local.close();
    fileSystem.close();
}

未完待續…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章