Java通道


通道(Channel)是資料源和Java程式之間的開放連線,用於執行I/O操作。Channel介面在java.nio.channels包中。通道(Channel)介面只宣告了兩個方法:close()isOpen()

各種渠道

ReadableByteChannel用於使用read()方法將資料從資料源讀取到位元組緩衝區中。WritableByteChannel用於使用write()方法將資料從位元組緩衝區寫入資料宿。

ByteChannel能夠分別使用read()write()方法讀取和寫入位元組資料。ScatteringByteChannel將資料從資料源讀取到多個位元組緩衝區中。 從已知的檔案格式或類似的資料源讀取資料是有用的,其中在一些固定長度的報頭中提供資料,隨後是可變長度的主體。

GatheringByteChannel從多個位元組緩衝區中寫出資料。

建立通道

要獲得一個通道,舊的方式使用java.io包中的類I/O來建立InputStreamOutputStream的物件。
java.nio.channels包中的Channels類是一個實用程式類,它有許多靜態方法將流轉換為通道,反之亦然。

Channels類還提供了將讀寫器轉換為通道的方法,反之亦然。例如,如果有一個名為myInputStream的輸入流物件,獲得一個ReadableByteChannel如下:

ReadableByteChannel rbc  = Channels.newChannel(myInputStream);

如果有一個名為rbcReadableByteChannel,可以獲得如下的基本InputStream物件:

InputStream myInputStream  = Channels.newInputStream(rbc);

FileInputStreamFileOutputStream類有一個稱為getChannel()的新方法來返回一個FileChannel物件。FileChannel用於讀取和寫入資料到檔案。從FileInputStream獲取的FileChannel物件以唯讀模式開啟。

FileInputStream fis  = new FileInputStream("test1.txt"); 
FileChannel fcReadOnly  = fis.getChannel(); // A  read-only  channel

FileOutputStream物件獲取的FileChannel物件以只寫模式開啟。

FileOutputStream fos   = new FileOutputStream("test1.txt"); 
FileChannel  fcWriteOnly = fos.getChannel(); // A  write-only  channel

如果從RandomAccessFile獲取一個FileChannel,它將以唯讀,只寫或讀寫模式開啟,這取決於建立RandomAccessFile物件的方式。
以下程式碼為不同種類的檔案流獲取FileChannel物件:

// read-only mode
RandomAccessFile  raf1 = new RandomAccessFile("test1.txt", "r"); 
FileChannel  rafReadOnly = raf1.getChannel(); // A  read-only  channel

// read-write mode
RandomAccessFile  raf2 = new RandomAccessFile("test1.txt", "rw"); 
FileChannel rafReadWrite = raf2.getChannel(); // A  read-write channel

讀/寫檔案

FileChannel物件維護位置變數作為緩衝區。FileChannelread()write()方法有兩種型別:相對位置讀/寫和絕對位置讀/寫。

當開啟一個FileChannel時,它的位置設定為0,這是檔案的開始。當使用相對read()方法從FileChannel讀取時,它的位置增加讀取的位元組數。
FileChannel讀取的絕對位置不會影響其位置。可以使用position()方法獲取FileChannel物件的當前位置值。使用position(int newPosition)方法將其位置設定為新位置。

通道也是可自動關閉的。如果使用try-with-resources語句來獲得一個通道,通道將被自動關閉,這樣就不用顯示地呼叫通道的close()方法。

以下程式碼從名為test1.txt的檔案中讀取文字。

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class Main {
  public static void main(String[] args) {
    File inputFile = new File("test1.txt");
    if (!inputFile.exists()) {
      System.out.println("The input file " + inputFile.getAbsolutePath()
          + "  does  not  exist.");
      System.out.println("Aborted the   file reading process.");
      return;
    }
    try (FileChannel fileChannel = new FileInputStream(inputFile).getChannel()) {
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      while (fileChannel.read(buffer) > 0) {
        buffer.flip();
        while (buffer.hasRemaining()) {
          byte b = buffer.get();
          System.out.print((char) b);
        }
        buffer.clear();
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

上面的程式碼生成以下結果。

The input file F:\website\yiibai\worksp\test1.txt  does  not  exist.
Aborted the   file reading process.

範例

以下程式碼顯示如何使用緩衝區和通道寫入檔案。

import java.io.File;
import java.nio.channels.FileChannel;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.io.FileOutputStream;

public class Main {
  public static void main(String[] args) {
    File outputFile = new File("test.txt");

    try (FileChannel fileChannel = new FileOutputStream(outputFile)
        .getChannel()) {
      String text = getText();
      byte[] byteData = text.toString().getBytes("UTF-8");
      ByteBuffer buffer = ByteBuffer.wrap(byteData);
      fileChannel.write(buffer);
    } catch (IOException e1) {
      e1.printStackTrace();
    }
  }

  public static String getText() {
    String lineSeparator = System.getProperty("line.separator");
    StringBuilder sb = new StringBuilder();
    sb.append("test");
    sb.append(lineSeparator);
    sb.append("test");
    sb.append(lineSeparator);

    sb.append("test");
    sb.append(lineSeparator);
    sb.append("test");

    return sb.toString();
  }
}

複製檔案的內容

可以使用緩衝區和通道來複製檔案。獲取原始檔和目標檔案的FileChannel物件,並對源FileChannel物件呼叫transferTo()方法或呼叫目標FileChannel物件上的transferFrom()方法。
以下程式碼顯示如何複製檔案。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;

public class Main {
  public static void main(String[] args) throws Exception {

    FileChannel sourceChannel = new FileInputStream("sourceFile").getChannel();
    FileChannel sinkChannel = new FileOutputStream("newFile").getChannel();

    // Copy source file contents to the sink file
    sourceChannel.transferTo(0, sourceChannel.size(), sinkChannel);
  }
}