NIO 中FileChannel可以理解為一個連接到文件的通道,可以通過FileChannel對文件進行讀寫; FileChannel沒有非阻塞模式,讀寫都只有阻塞的方式;
RandomAccessFile fromFile = new RandomAccessFile("D:/fromFile.txt", "rw");//獲取channelFileChannel fromChannel = fromFile.getChannel();從FileChannel讀取數據
首先,分配一個Buffer。從FileChannel中讀取的數據將被讀到Buffer中。 然后,調用FileChannel.read()方法。該方法將數據從FileChannel讀取到Buffer中。read()方法返回的int值表示了有多少字節被讀到了Buffer中。如果返回-1,表示到了文件末尾。如:ByteBuffer buf = ByteBuffer.allocate(1024*4); int bytesRead = inChannel.read(buf);向FileChannel寫數據
使用FileChannel.write()方法向FileChannel寫數據,該方法的參數是一個Buffer。如:String newData = "New String to write to file..." + System.currentTimeMillis(); ByteBuffer buf = ByteBuffer.allocate(1024*4); buf.clear(); buf.put(newData.getBytes()); buf.flip(); while(buf.hasRemaining()) { channel.write(buf); } 注意FileChannel.write()是在while循環中調用的。因為無法保證write()方法一次能向FileChannel寫入多少字節,因此需要重復調用write()方法,直到Buffer中已經沒有尚未寫入通道的字節。關閉FileChannel
用完FileChannel后必須將其關閉。如:channel.close();FileChannel的position方法
有時可能需要在FileChannel的某個特定位置進行數據的讀/寫操作??梢酝ㄟ^調用position()方法獲取FileChannel的當前位置。 也可以通過調用position(long pos)方法設置FileChannel的當前位置。 這里有兩個例子:long pos = channel.position(); channel.position(pos +123); 如果將位置設置在文件結束符之后,然后試圖從文件通道中讀取數據,讀方法將返回-1 —— 文件結束標志。 如果將位置設置在文件結束符之后,然后向通道中寫數據,文件將撐大到當前位置并寫入數據。這可能導致“文件空洞”,磁盤上物理文件中寫入的數據間有空隙。FileChannel的size方法
FileChannel實例的size()方法將返回該實例所關聯文件的大小。如:long fileSize = channel.size();FileChannel的truncate方法
可以使用FileChannel.truncate()方法截取一個文件。截取文件時,文件將中指定長度后面的部分將被刪除。如:channel.truncate(1024); 這個例子截取文件的前1024個字節。FileChannel的force方法
FileChannel.force()方法將通道里尚未寫入磁盤的數據強制寫到磁盤上。出于性能方面的考慮,操作系統會將數據緩存在內存中,所以無法保證寫入到FileChannel里的數據一定會即時寫到磁盤上。要保證這一點,需要調用force()方法。 force()方法有一個boolean類型的參數,指明是否同時將文件元數據(權限信息等)寫到磁盤上。 下面的例子同時將文件數據和元數據強制寫到磁盤上:channel.force(true);示例
對于資源文件,操作完都是需要關閉的;以下是一個完成讀寫實例代碼:public static void main(String[] args) throws IOException { RandomAccessFile fromFile = new RandomAccessFile("src//a.txt", "rw"); RandomAccessFile toFile = new RandomAccessFile("src//b.txt", "rw"); // 獲取channel FileChannel fromChannel = fromFile.getChannel(); FileChannel toChannel = toFile.getChannel(); // 定義緩沖大小 int bufSize = 1024 * 4; // 定義緩沖 ByteBuffer byteBuffer = ByteBuffer.allocate(bufSize); int len = 0; // 將數據從源channel寫入到緩沖區 while ((len = fromChannel.read(byteBuffer)) != -1) { // 切換到讀模式 byteBuffer.flip(); while (byteBuffer.hasRemaining()) { // 讀取緩沖區數據寫到目標channel toChannel.write(byteBuffer); } // 清空緩沖 byteBuffer.clear(); } // 釋放資源 toChannel.close(); fromChannel.close(); toFile.close(); fromFile.close();}緩沖區的hasRemainning方法返回一個boolean,標識緩沖區中是否還有數據沒有讀取,寫入FileChannel必須在一個循環中,因為write方法無法保證緩沖區的數據一次全部寫入;通過FileChannel直接傳輸
在兩個通道間傳輸數據,如果其中有一個是FileChannel,可以不使用緩沖區讀出、寫入的方式進行傳輸,可以使用FileChannel的transferFrom 和 transferTo 方法;1. transferFrom
public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException; 該方法可以從其他通道直接將數據傳輸到fileChannel,position參數表示從position開始先目標文件寫入數據,count參表示最多寫入的數據量,但是不一定會寫入這個參數的數據量,如果來源通道沒有那么多可讀數據的話;該方法返回一個long值表示成功寫入通道的數據量;2. transferTo
public abstract long transferTo(long position, long count, WritableByteChannel target) throws IOException; 改方法與transferFrom相反,是將FileChannel的數據直接寫入到目標通道,position參數表示從position位置開始讀取數據寫入,count參數表示要寫入數據的大小,但是不一定寫入這個參數的數據量,如果這個fileChannel沒有那么多數據可以讀取的話;該方法返回一個long值,表示實際寫入目標通道的數據量;以下是一個通過transferFrom/transferTo方法復制文件的實例代碼:public static void main(String[] args) throws IOException { RandomAccessFile fromFile = new RandomAccessFile("src//a.txt", "rw"); RandomAccessFile inFile = new RandomAccessFile("src//a.txt", "rw"); FileChannel fromChannel = fromFile.getChannel(); FileChannel inChannel = inFile.getChannel(); int position = 0; long count = fromChannel.size(); long successCount = inChannel.transferFrom(fromChannel, position, count); //long successCount = fromChannel.transferTo(position, count, inChannel); System.out.PRintln(successCount); }文件鎖定
FileChannel的鎖,與一般的對象鎖相似,都是勸告式鎖;獲取鎖后,它不阻止任何形式的數據訪問,而是與對象鎖相似,程序中可以通過它進行多線程間協調(線程安全處理);
通過FileChannel的lock方法,可以鎖定整個文件或指定一部分; 請求獲取的鎖有兩種:共享鎖和排它鎖;共享鎖可以獲取多個,而排它鎖只能獲取到一個;由于不同的操作系統對文件系統鎖的實現方式不一樣,有些實現了共享鎖,而另外一些只實現了排它鎖;
以下是一個獲取文件鎖的實例代碼,可以通過同時運行多個觀察效果:
public static void main(String[] args) throws Exception { RandomAccessFile fromFile = new RandomAccessFile("src//a.txt", "rw"); FileChannel fromChannel = fromFile.getChannel(); System.out.println("trying to lock file..."); int start = 0; int end = 10; FileLock fileLock = fromChannel.lock(start, end, false); System.out.println("after lock"); System.out.println("pause..."); Thread.sleep(8000); System.out.println("releasing file lock"); fileLock.release(); System.out.println("after release."); fromChannel.close(); fromFile.close(); }lock 方法中,start、end參數指定鎖定文件的部分,第三個boolean參數,true:表示獲取共享鎖,false:表示獲取排它鎖; 還有一個lock()無參的方法,實際是調用:public final FileLock lock() throws IOException { return lock(0L, Long.MAX_VALUE, false);}由于不是全部操作系統都支持共享鎖,所以就算請求獲取共享鎖,但是也可能會返回一個排它鎖,可以通過isShare方法確定鎖類型:fileLock.isShared();對此,我們不如直接只使用排它鎖,而不是進行isShare邏輯的判斷。
新聞熱點
疑難解答