Channel
基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。这里有个图示:
JAVA NIO中的一些主要Channel的实现:
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
其中后三个主要用于网络编程,第一个是对文件的操作。
FileChannel类
Java NIO中的FileChannel是一个连接到文件的通道。可以通过文件通道读写文件。
FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下。
一、创建FileChannel
主要有两种创建方式:
第一种:使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例。
第二种:JDK1.7之后才能使用, FileChannel.open()方法。
//InputStream、OutputStream或RandomAccessFile,创建FileChannel
RandomAccessFile raf = new RandomAccessFile("D:\\Java\\gp.txt", "r");
FileChannel channel = raf.getChannel();
FileInputStream fis = new FileInputStream("D:\\Java\\gp.txt");
FileChannel channel1 = fis.getChannel();
FileChannel channel2 = new FileOutputStream("D:\\Java\\gp.txt").getChannel();
//FileChannel.open()方法创建Channel
//Paths.get()获取路径,StandardOpenOption.READ表示读的模式
FileChannel readChannel = FileChannel.open(Paths.get("D:\\Java\\gp.jpg"), StandardOpenOption.READ);
//CREATE,如果文件存在就不创建,不存在就创建
FileChannel writeChanne1 = FileChannel.open(Paths.get("D:\\Java\\gp.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//APPEND表示追加写入
FileChannel fileChannel = FileChannel.open(Paths.get("D:\\Java\\gp.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
二、从FileChannel读取数据
//读取数据,但是隐含“写”的过程,另外如果读到流的结尾,可能返回0或-1,
int read(ByteBuffer dst);
long read(ByteBuffer[] dsts);
long read(ByteBuffer[] dsts, int offset, int length);
long read(ByteBuffer dst, long position);
long size();
读取的方式是将硬盘的数据读出来,然后放到(写入put)缓冲区,所以隐含写的过程,最后要从缓冲区读取数据,这时候必须 Flip() 转读取模式。
public class FileChannleDemo {
public static void main(String[] args) throws IOException {
//创建RandomAccessFile
RandomAccessFile raf = new RandomAccessFile("D:\\Java\\gp4.txt", "r");
//创建管道
FileChannel channel = raf.getChannel();
//读取
ByteBuffer buf = ByteBuffer.allocate(1024);
/**
* 该方法将硬盘的数据读出来,然后放到缓冲区
* 隐含写的过程
* 然后变成读模式,把数据取出
*/
while (channel.read(buf) > 0) {
buf.flip();
System.out.println(new String(buf.array(), 0, buf.limit()));
buf.clear();
}
channel.close();
raf.close();
}
}
三、向FileChannel写数据
使用FileChannel.write()方法向FileChannel写数据,该方法的参数是一个Buffer。
注意:fileChannel.write()是在while循环中调用的。因为无法保证write()方法一次能向fileChannel写完所有字节,因此需要重复调用write()方法,直到Buffer中已经没有尚未写入通道的字节。
public class FileChannelWrite {
public static void main(String[] args) throws IOException {
FileChannel fileChannel = FileChannel.open(Paths.get("D:\\Java\\Demo.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
//写入
ByteBuffer buffer = ByteBuffer.allocate(1024);
for (int i = 0; i < 10; i++) {
buffer.put("\r\n好好学习".getBytes());
buffer.flip();
fileChannel.write(buffer);
buffer.clear();
}
fileChannel.close();
}
}
复制
public class FileChannelCopy {
public static void main(String[] args) throws IOException {
//读取通道
FileChannel readChannel = FileChannel.open(Paths.get("D:\\Java\\gp.jpg"), StandardOpenOption.READ);
//写入通道
FileChannel writeChanne1 = FileChannel.open(Paths.get("D:\\Java\\gp2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//读取写入
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (readChannel.read(buffer) > 0) {
buffer.flip();
writeChanne1.write(buffer);
buffer.clear();
}
readChannel.close();
writeChanne1.close();
}
}
四、使用内存映射文件复制大文件
内存映射文件属于直接缓冲区,直接在内存开辟空间,如果文件超过2G,需要分多个文件映射,速度快。
文件整体复制
/**
* 2G以下文件的整体复制
*/
public class Demo3 {
public static void main(String[] args) throws IOException {
FileChannel readChannel = FileChannel.open(Paths.get("D:\\Java\\blog.wmv"), StandardOpenOption.READ);
FileChannel writeChannel = FileChannel.open(Paths.get("D:\\Java\\Blog\\shipin.wmv"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//读取通道获取内存映射map
//第一个参数是模式,由FileChannel静态方法调用,再调用只读模式
//后面参数是文件的读取起始位置和文件的大小
MappedByteBuffer map = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, readChannel.size());
writeChannel.write(map);
readChannel.close();
writeChannel.close();
System.out.println("复制完毕");
}
}
大文件拆分复制
其实就是更改文件读取的起始位置和文件的大小,注意,最后的参数是【文件大小】,而不是文件的末尾,计算要准确。
public class Demo3 {
public static void main(String[] args) throws IOException {
FileChannel readChannel = FileChannel.open(Paths.get("D:\\Java\\Blog\\shipin.wmv"), StandardOpenOption.READ);
FileChannel writeChannel = FileChannel.open(Paths.get("D:\\Java\\Blog\\shipin2.wmv"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
MappedByteBuffer map1 = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, 1024 * 1024 * 40);
MappedByteBuffer map2 = readChannel.map(FileChannel.MapMode.READ_ONLY, 1024 * 1024 * 40, 1024 * 1024 * 40);
MappedByteBuffer map3 = readChannel.map(FileChannel.MapMode.READ_ONLY, 1024 * 1024 * 80, readChannel.size() - 1024 * 1024 * 80);
writeChannel.write(map1);
writeChannel.write(map2);
writeChannel.write(map3);
readChannel.close();
writeChannel.close();
System.out.println("复制完毕");
}
}