Netty學習(3):文件操作


概述

在 Netty學習(2)中,我們先淺淺認識了 NIO 的3大核心組件,現在就讓我們針對其深入學習,通過一些簡單的文件操作來深入理解其中的 BufferChannel 的概念。

文件寫入

將內存中的數據寫入到文件中,如果文件不存在,那么就新建文件。

// 數據 -> 文件
    private static void dataToFile(String data, String filePath) {
        // 構建輸出流  ->   從輸出流中獲取 channel
        try (FileOutputStream fileOutputStream = new FileOutputStream(filePath);
             FileChannel fileChannel = fileOutputStream.getChannel()) {

            // 設置緩沖區
            ByteBuffer byteBuffer = ByteBuffer.allocate(BYTE_BUFFER_LENGTH);

            // 將需要讀寫的數據放到緩沖區
            int i = 0;
            int length = data.getBytes().length;
            // 一次就可以讀完
            if (BYTE_BUFFER_LENGTH > data.getBytes().length) {
                byteBuffer.put(data.getBytes(), i, data.getBytes().length);
                byteBuffer.flip();
                fileChannel.write(byteBuffer);
            } else {
                // 一次讀不完  需要循環讀取
                for (int temp = 0; temp < data.getBytes().length; temp += BYTE_BUFFER_LENGTH) {
                    byteBuffer.clear();
                    byteBuffer.put(data.getBytes(), temp, BYTE_BUFFER_LENGTH);
                    // 翻轉緩沖區,可以對外讀
                    // 這里的 flip() 是重點,其可以將Buffer的屬性重置,可以對外寫
                    byteBuffer.flip();
                    // 將緩沖區內的數據寫到 channel中
                    fileChannel.write(byteBuffer);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

這樣,我們就寫完了一個文件寫入的函數,在需要時傳入指定的字符串即可。

文件讀取

從文件中讀取數據,並將其輸出到控制台中。

 // 文件 -> 內存
    private static void dataFromFile(String filePath) {
        File file = new File(filePath);
        // 從輸入流中獲取 channel
        try (FileInputStream fileInputStream = new FileInputStream(file);
             FileChannel channel = fileInputStream.getChannel()) {

            // 分配緩沖區
            ByteBuffer byteBuffer = ByteBuffer.allocate(BYTE_BUFFER_LENGTH);
            StringBuilder result = new StringBuilder();
            while (true) {
                byteBuffer.clear();
                // 將 channel數據寫到buffer中
                int read = channel.read(byteBuffer);
                // 因為byteBuffer大小原因,因此需要用一個中間字符串接受一下
                result.append(new String(byteBuffer.array()));
                if (read == -1) {
                    break;
                }
            }

            logger.info("從文本讀取結果:{}", result);
        } catch (Exception e) {
            logger.error("文件讀取錯誤,錯誤原因 :{}", e);
        }
    }

文件拷貝

用 NIO 來完成文件拷貝,有兩種實現方式,一種是用 Buffer 完成兩個文件之間數據的轉移,另一種是直接使用 Channel 來完成文件復制。

Buffer 完成

通過 Buffer 來完成文件復制,步驟如下:

  1. 獲取源文件(source)和目標文件(target)的 channel;
  2. 設置緩沖區;
  3. 在循環中,通過緩沖區,將 source 的數據寫入到 target 的 channel 中,完成寫入,即復制成功。
// 將兩個channel通過byteBuffer進行轉移
    private static void copyFileUseBuffer(String sourceFilePath, String targetFilePath) {
        File source = new File(sourceFilePath);
        File target = new File(targetFilePath);
        // 獲取文件輸入輸出流
        // 從輸入輸出流中獲取輸入輸出 channel

        try (FileInputStream fileInputStream = new FileInputStream(source);
             FileOutputStream fileOutputStream = new FileOutputStream(target);
             FileChannel fileInputStreamChannel = fileInputStream.getChannel();
             FileChannel fileOutputStreamChannel = fileOutputStream.getChannel()) {

            // 分配緩沖區
            ByteBuffer byteBuffer = ByteBuffer.allocate(BYTE_BUFFER_LENGTH);
            // 將輸入流中的數據寫到緩沖區
            // 這里需要循環讀取,如果是大文件,不能直接建立一個很大的內存空間,直接全部放進去,並且還可能放不進去
            while (true) {
                byteBuffer.clear();

                int read = fileInputStreamChannel.read(byteBuffer);
                if (read == -1) {
                    break;
                }
                // 翻轉緩沖區
                byteBuffer.flip();

                // 將翻轉后可以對外寫的緩存區的內容寫到輸出流,從而形成文件
                fileOutputStreamChannel.write(byteBuffer);
            }
        } catch (Exception e) {
            logger.error("文件復制錯誤,錯誤原因 :{0}", e);
        }
   

Channel 完成

但其實,Java 官方也考慮到這個需求,其內置了一個通道復制的函數,可以直接完成復制。

// 直接用channel的復制完成文件復制
    private static void copyFileUseChannelTransfer(String sourceFilePath, String targetFilePath) {
        File source = new File(sourceFilePath);
        File target = new File(targetFilePath);
        // 獲取文件輸入輸出流
        // 從輸入輸出流中獲取輸入輸出 channel
        try (FileInputStream fileInputStream = new FileInputStream(source);
             FileOutputStream fileOutputStream = new FileOutputStream(target);
             FileChannel fileInputStreamChannel = fileInputStream.getChannel();
             FileChannel fileOutputStreamChannel = fileOutputStream.getChannel()) {

            // 直接將輸入channel復制到輸出channel
            fileOutputStreamChannel.transferFrom(fileInputStreamChannel, fileInputStreamChannel.position(), fileInputStreamChannel.size());

        } catch (Exception e) {
            logger.error("文件復制錯誤,錯誤原因 :{0}", e);
        }
    }

總結

本文,我們通過文件的讀取,寫入,復制,從而理解了 Buffer 和 Channel 的作用和使用方式,在后續的網絡編程中,我們還要用到這些操作方式,以便逐步深入到 Netty 的學習范圍。

本文中代碼已上傳到 GitHub 上,地址為 https://github.com/wb1069003157/nettyPre-research ,歡迎大家來討論,探討。

iceWang公眾號

文章在公眾號「iceWang」第一手更新,有興趣的朋友可以關注公眾號,第一時間看到筆者分享的各項知識點,謝謝!筆芯!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM