java nio系列教程(2)---channel(通道介紹)和使用
阿新 • • 發佈:2018-12-17
大家推薦個靠譜的公眾號程式設計師探索之路,大家一起加油
package com.zzh.buffer; import com.google.common.collect.Lists; import org.junit.jupiter.api.Test; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.net.DatagramSocket; import java.net.ServerSocket; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.IntBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.List; import java.util.Map; import java.util.SortedMap; /** * 一.通道(channel):用於源節點與目標節點的連線.在java nio中負責緩衝區中資料的傳輸.channel本身不儲存資料,因此需要配合緩衝區進行傳輸. * <p> * 二.通道的主要實現類 * java.nio.channels.Channel介面: * |--FileChannel * |--SocketChannel * |--ServerSocketChannel * |--DatagramChannel * 三.獲取通道 * 1.java 針對支援通道的類提供了getChannel()方法 * 本地IO: * FileInputStream/FileOutputStream * RandomAccessFile * <p> * 網路IO * Socket * ServerSocket * DatagramSocket * 2. 1.7之後 針對各個通道提供了靜態方法open() * 3. 1.7後, Files工具類的newByteChannel */ public class TestChannel { public static void main(String[] args) { System.currentTimeMillis(); } /** * 非直接緩衝區 */ @Test public void test1() { FileInputStream inputStream = null; FileOutputStream outputStream = null; FileChannel inChannel = null; FileChannel outChannel = null; try { inputStream = new FileInputStream("1.jpg"); outputStream = new FileOutputStream("2.jpg"); //1.獲取通道 inChannel = inputStream.getChannel(); outChannel = outputStream.getChannel(); //分配緩衝區 ByteBuffer byteBuffer = ByteBuffer.allocate(1024); while (inChannel.read(byteBuffer) != -1) { //切換到讀資料模式 byteBuffer.flip(); //將緩衝區中的資料寫入通道中 outChannel.write(byteBuffer); //清空緩衝區 byteBuffer.clear(); } } catch (Exception e) { } finally { if (outChannel != null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (inChannel != null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 記憶體對映檔案 */ @Test public void test2() { FileChannel inChannel = null; FileChannel outChannel = null; try { inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ); /** * 注意 StandardOpenOption.CREATE CREATE_NEW 這兩個的區別 create 不管之前是否存在都是建立一個 create_new 如果檔案已存在就會報錯 */ outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE); //這個記憶體對映檔案是隻讀模式 從0開始 到inChannel.size()結束 MappedByteBuffer inMap = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size()); /** * 這個記憶體對映檔案是讀寫模式 從0開始 到inChannel.size()結束 * MappedByteBuffer沒有單獨的寫模式 只有READ_WRITE讀寫模式 對應的通道StandardOpenOption 應該有讀和寫 */ MappedByteBuffer outMap = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size()); byte[] bytes = new byte[inMap.limit()]; inMap.get(bytes); outMap.put(bytes); } catch (IOException e) { e.printStackTrace(); } finally { if (outChannel != null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (inChannel != null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 通道之間的直接傳輸 */ @Test public void test3() { FileChannel inChannel = null; FileChannel outChannel = null; try { inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ); /** * 注意 StandardOpenOption.CREATE CREATE_NEW 這兩個的區別 create 不管之前是否存在都是建立一個 create_new 如果檔案已存在就會報錯 */ outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE); /** * 輸入通道 從0,開始到inChannel.size()結束,目標通道outChannel */ // inChannel.transferTo(0, inChannel.size(), outChannel); /** * 輸出通道 從inChannel通道來 0到inChannel.size() */ outChannel.transferFrom(inChannel, 0, inChannel.size()); } catch (IOException e) { e.printStackTrace(); } finally { if (outChannel != null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (inChannel != null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 分散(Scatter) 與 聚集(Gather) * 分散讀取(Scattering Reads):將通道中的資料分散到多個緩衝區中 * 聚集寫入(Gathering Writes):將多個緩衝區的資料聚集到通道中 */ @Test public void test4() { RandomAccessFile randomAccessFile = null; RandomAccessFile outFile = null; FileChannel inChannel = null; FileChannel outChannel = null; try { randomAccessFile = new RandomAccessFile("1.jpg", "rw"); //獲取通道 inChannel = randomAccessFile.getChannel(); //分配緩衝區 ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024); ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024); ByteBuffer[] byteBuffers = {byteBuffer1, byteBuffer2}; //分散讀取 inChannel.read(byteBuffers); //切換到 讀資料模式 for (ByteBuffer byteBuffer : byteBuffers) { byteBuffer.flip(); } outFile = new RandomAccessFile("3.jpg", "rw"); outChannel = outFile.getChannel(); //聚集寫入 outChannel.write(byteBuffers); } catch (Exception e) { e.printStackTrace(); } finally { if (randomAccessFile != null) { try { randomAccessFile.close(); } catch (IOException e) { e.printStackTrace(); } } if (outFile != null) { try { outFile.close(); } catch (IOException e) { e.printStackTrace(); } } if (outChannel != null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (inChannel != null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 嘗試根據檔案大小進行分散讀取,聚集寫入 * @throws IOException */ @Test public void test5() throws IOException{ RandomAccessFile raf1 = new RandomAccessFile("1.jpg", "rw"); //獲取通道 FileChannel channel1 = raf1.getChannel(); //獲取檔案大小 單位是byte long size = channel1.size(); //把大小分配到16個bytebuffer中 為什麼是16個可以看下FileChannel的write方法可以發現 裡面的迴圈 小於IOV_MAX=16 int num = ((int)size / 16) + 1; List<ByteBuffer> byteBufferList = Lists.newArrayList(); for (int i = 0;i < 16;i++){ ByteBuffer buf = ByteBuffer.allocate(num); byteBufferList.add(buf); } //分散讀取 ByteBuffer[] bufs = new ByteBuffer[byteBufferList.size()]; bufs = byteBufferList.toArray(bufs); channel1.read(bufs); //全部切換到讀資料模式 for (ByteBuffer byteBuffer : bufs) { byteBuffer.flip(); } //聚集寫入 RandomAccessFile raf2 = new RandomAccessFile("5.jpg", "rw"); FileChannel channel2 = raf2.getChannel(); /** * 這裡有個坑 不是所有的bufs都會寫進去 * 寫的時候 for迴圈最大是16 也就是 bufs的16之後的都不會寫進去 */ channel2.write(bufs); } /** * 字符集 編碼與解碼 * 編碼:字串->位元組陣列 * 解碼:位元組陣列->字串 */ @Test public void test6(){ //所有字符集 // SortedMap<String, Charset> map = Charset.availableCharsets(); // for (Map.Entry<String, Charset> entity : map.entrySet()){ // System.out.println(entity.getKey()+"--->"+entity.getValue()); // } try { Charset gbk = Charset.forName("GBK"); //獲取編碼器 CharsetEncoder charsetEncoder = gbk.newEncoder(); //獲取解碼器 CharsetDecoder charsetDecoder = gbk.newDecoder(); CharBuffer charBuffer = CharBuffer.allocate(1024); charBuffer.put("趙志恆"); charBuffer.flip(); //編碼 解出來的bytebuffer已經是 切換成讀模式了 不用在切換 ByteBuffer encode = charsetEncoder.encode(charBuffer); //解碼 解出來的CharBuffer已經是 切換成讀模式了 不用在切換 CharBuffer decode = charsetDecoder.decode(encode); System.out.println(decode.toString()); Charset utf8 = Charset.forName("UTF-8"); CharsetDecoder utf8Decoder = utf8.newDecoder(); //encode 重置為可讀 encode.rewind(); //此處這樣並不能截出來 會丟擲異常 MalformedInputException(此物件表示格式錯誤的輸入錯誤) CharBuffer decode1 = utf8Decoder.decode(encode); } catch (CharacterCodingException e) { e.printStackTrace(); } finally { } } }