版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yueloveme/article/details/84988010
大家推荐个靠谱的公众号程序员探索之路,大家一起加油
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 {
}
}
}