NIO介绍
在讲解Channel之前,首先了解一下NIO, Java NIO全称java non-blocking IO,是从Java 1.4版本开始引入的一个新的IO API(New IO),可以替代标准的Java IO API,NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同。IO与 NIO区别:
IO | NIO |
---|---|
面向流(Stream Orientend) | 面向缓冲区(Buffer Orientend) |
阻塞IO(Blocking IO ) | 非阻塞IO(Non Blocking IO) |
选择器(Selector) |
NIO支持面向缓冲区的、基于通道的IO操作并以更加高效的方式进行文件的读写操作,其核心API为Channel(通道),Buffer(缓冲区), Selector(选择器)。Channel负责传输,Buffer负责存储 。
通道(Channel )
通道表示打开到 IO 设备(例如:文件、套接字)的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。
Channel相比IO中的Stream更加高效,可以异步双向传输,但是必须和buffer一起使用。
主要实现类
FileChannel,读写文件中的数据。
SocketChannel,通过TCP读写网络中的数据。
ServerSockectChannel,监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。
DatagramChannel,通过UDP读写网络中的数据。
他们的使用方法会在代码中体现出来。
主要获取方式
1、java针对支持通道的类提供了getChannel()方法
本地io:
FileInputStreanm/FileOutputStream
RandomAccessFile
网络io:
Socket
ServerSocket
DatagramSocket
FileInputStream fis = new FileInputStream("D:\\1.jpg");
FileChannel inChannel = fis.getChannel();
2、 jdk1.7的nio2只对各个通道提供了一个静态方法open()
FileChannel inChannel = FileChannel.open(Paths.get("D:\\1.jpg"), StandardOpenOption.READ);
通道之间的数据传输
1、read&write
//将 Buffer 中数据写入 Channel
outChannel.write(buff)
//从 Channel 读取数据到 Buffer
inChannel.read(buff)
2、transferFrom
从源信道读取字节到这个通道的文件中。如果源通道的剩余空间小于 count 个字节,则所传输的字节数要小于请求的字节数。这种方法可能比从源通道读取并写入此通道的简单循环更有效率。
@param SRC 源通道
@param position 调动开始的文件内的位置,必须是非负的
@param count 要传输的最大字节数,必须是非负
@return 传输文件的大小(单位字节),可能为零,
public abstract long transferFrom(ReadableByteChannel src, long position, long count) throws IOException;
//复制图片,利用直接缓存区
public void test() throws Exception{
FileChannel inChannel = FileChannel.open(Paths.get("D:\\1.jpg"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("D:\\2.jpg"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
outChannel.transferFrom(inChannel,0, inChannel.size());
inChannel.close();
outChannel.close();
}
3、transferTo
将字节从这个通道的文件传输到给定的可写字节通道。
@param position 调动开始的文件内的位置,必须是非负的
@param count 要传输的最大字节数,必须是非负
@param target 目标通道
@return 传输文件的大小(单位字节),可能为零,
public abstract long transferTo(long position, long count, WritableByteChannel target) throws IOException;
//复制图片,利用直接缓存区
public void test2() throws Exception{
FileChannel inChannel = FileChannel.open(Paths.get("D:\\1.jpg"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("D:\\3.jpg"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
inChannel.transferTo(0, inChannel.size(), outChannel);
inChannel.close();
outChannel.close();
}
}
常用方法
方 法 | 描 述 |
---|---|
int read(ByteBuffer dst) | 从Channel到中读取数据到ByteBuffer |
long read(ByteBuffer[] dsts) | 将Channel到中的数据“分散”到ByteBuffer[] |
int write(ByteBuffer src) | 将ByteBuffer到中的数据写入到Channel |
long write(ByteBuffer[] srcs) | 将ByteBuffer[]到中的数据“聚集”到Channel |
long position() | 返回此通道的文件位置 |
FileChannel position(long p) | 设置此通道的文件位置 |
long size() | 返回此通道的文件的当前大小 |
FileChannel truncate(long s) | 将此通道的文件截取为给定大小 |
void force(boolean metaData) | 强制将所有对此通道的文件更新写入到存储设备中 |
示例代码
//利用通道完成文件的复制,非直接缓冲区
@Test
public void test() throws Exception{
FileInputStream fis = new FileInputStream("D:\\1.jpg");
FileOutputStream fos = new FileOutputStream("D:\\2.jpg");
//获取通道
FileChannel inChannel = fis.getChannel();
FileChannel outChannel = fos.getChannel();
//分配指定大小缓存区
ByteBuffer buff = ByteBuffer.allocate(1024);// position 0 ,limit 1024
//将通道的数据存入缓存区
while(inChannel.read(buff)!=-1){// position 1024 ,limit 1024 ,相当于put
//切换读模式
buff.flip();//position 0 ,limit 1024
//将缓存去的数据写入通道
outChannel.write(buff);//position 1024 ,limit 1024,相当于get
//清空缓冲区
buff.clear();//position 0 ,limit 1024
}
outChannel.close();
inChannel.close();
fis.close();
fos.close();
}
//利用通道完成文件的复制,直接缓冲区,利用物理内存映射文件
//会出现文件复制完了,但程序还没结束,原因是JVM资源还在用,当垃圾回收机制回收之后程序就会结束,不稳定
@Test
public void test1() throws Exception{
FileChannel inChannel = FileChannel.open(Paths.get("D:\\1.jpg"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("D:\\4.jpg"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
//内存映射文件
MappedByteBuffer inMapBuff = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());//==allocateDirect
MappedByteBuffer outMapBuff = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
byte[] by = new byte[inMapBuff.limit()];
inMapBuff.get(by);
outMapBuff.put(by);
outChannel.close();
inChannel.close();
}