詳解NIO中FileChannel文件流的簡單使用

前言

在文章NIO 下的 ByteBuffer簡單學習 中, 我們有寫過一個簡單的文件讀取案例, 在案例中有使用到 Buffer 和 Channel, 關於 Buffer 的簡單使用可以看下面兩篇文章

  • NIO 下的 ByteBuffer簡單學習
  • 最簡單的 NIO 粘包解析與分析

關於 Channel 主要分為以下幾種, 本篇文章是對 FileChannel 的講解:

  • FileChannel: 文件通道, 主要用於對文件的讀寫
  • DatagramChannel: 數據包通道, 可以發送和接受 UPD 的數據包
  • SocketChannel: 套接字通過, 接收 TCP 數據包的讀寫
  • ServerSocketChannel: 服務器套接字通道, 監聽新進來的 TCP 連接, 為每一個新連接都創建一個 SocketChannel

FileChannel

註意: FileChannel 隻能工作在阻塞模式下

新建

FileChannel 是一個抽象類, 所以不能直接創建對象

public abstract class FileChannel 
    extends AbstractInterruptibleChannel
    implements SeekableByteChannel, GatheringByteChannel,ScatteringByteChannel
    {
        /**
         * Initializes a new instance of this class.
         * */
         protected FileChannel(){}

創建一個 FileChannel 有以下三種方式:

  • 創建一個 FileInputStream 對象, 但是該對象獲取到的 Channel 隻能讀取
  • 創建一個 FileOutputStream 對象, 但是該對象獲取到的 Channel 隻能寫入
  • 創建一個 RandomAccessFile 對象, 該對象能否讀寫是根據構造 RandomAccessFile 時設置的讀寫模式設定的

註意: Channel 使用之後必須關閉
不主動 close 並且 未觸發gc , 那麼 連接句柄 將被 一直占用, 如果此時使用的是連接池方式, 將造成連接池中的連接不能及時的被回收問題

在調用 FileInputStream 、 FileOutputStream 和 RandomAccessFile 的 close 方法會間接調用 Channel 的 close 方法

實現文件的讀寫

通過以下代碼可以簡單的通過 FileInputStream 和 FileOutputStream 來實現對文件的讀寫

public static void main(String[] args) {
    try(
        FileChannel inputChannel = new FileInputStream( name: "test1.txt").getChannel();
        FileChannel outputChannel = new FileOutputStream( name: "test2.txt").getChannel()
        ){
            //  inputChannel 流讀取到的內容通過 outputChannel 傳輸到指定的地址
            inputChannel.transferTo( position: 0,inputChannel.size(),outputChannel);
        }catch (Exception e){
           e.printstackTrace();
        }
}

但是這裡有個問題, FileChannel 的 transferTo 隻能傳輸 2G 以內的數據, 超過 2G 就傳輸不瞭瞭,

下面是 FileChannel 的 tarnsferTo 方法, 可以看到他是存在返回值的, 這個返回值就代表著還剩下多少字節的內容沒有進行傳輸, 所以我們可以使用一個 for 循環來對當前的代碼進行改進

改進後的代碼如下所示

public static void main(String[] args) {
    try(
        FileChannel inputChannel = new FileInputStream( name: "test1.txt").getChannel();
        FileChannel outputChannel = new FileOutputStream( name: "test2.txt").getChannel()
        ){
            //獲取到 輸入流 的大小
            long size = inputChannel.size();
            // res 代表剩餘多少字節沒有進行傳輸
            for (long res = size;res > 0; ){
                //將 inputChannel 流讀取到的內容通過 outputChannel 傳輸到指定的地址
                // 效率高,底層會利用作系統的 零拷貝 進行優化,但是一次隻能傳輸 2G 的數據
                // 該方法返回值為 剩餘未傳輸的 字節數
                res = inputChannel.transferTo( position: 0,inputChannel.size(), outputChannel);
            }
        }catch (Exception e){
           e.printstackTrace();
        }
}

本篇文章所有代碼

public static void main(String[] args) {
    try(
            FileChannel inputChannel = new FileInputStream("test1.txt").getChannel();
            FileChannel outputChannel = new FileOutputStream("test2.txt").getChannel()
    ){
        // 獲取到 輸入流 的大小
        long size = inputChannel.size();
        // res 代表剩餘多少字節沒有進行傳輸
        for (long res = size; res > 0; ){
            // 將 inputChannel 流讀取到的內容通過 outputChannel 傳輸到指定的地址
            // 效率高, 底層會利用操作系統的 零拷貝 進行優化, 但是一次隻能傳輸 2G 的數據
            // 該方法返回值為 剩餘未傳輸的 字節數
            res = inputChannel.transferTo(0, inputChannel.size(), outputChannel);
        }
    }catch (Exception e){
        e.printStackTrace();
    }
}

到此這篇關於詳解NIO中FileChannel文件流的簡單使用的文章就介紹到這瞭,更多相關FileChannel使用內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: