FileCopyRunner接口,定义了Copy文件的接口,等下在测试类中使用匿名内部类来实现。java
package nio.channel; import java.io.File; public interface FileCopyRunner { void copyFile(File source , File target); }
测试类:web
benchmark()
:Copy文件ROUNDS(5)
次,而且返回耗费的平均时间(1.0F)*elapsed / ROUNDS
。close()
:关闭资源。完整代码(下面说四种方法):编程
package nio.channel; import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class FileCopyDemo { private static final int ROUNDS = 5; private static void benchmark(FileCopyRunner test , File sourse , File target){ long elapsed = 0L; for (int i = 0; i < ROUNDS; i++) { long startTime = System.currentTimeMillis(); test.copyFile(sourse , target); elapsed += System.currentTimeMillis() - startTime; target.delete(); } System.out.println(test+":"+(1.0F)*elapsed / ROUNDS); } public static void close(Closeable closeable){ if(closeable != null){ try { closeable.close(); } catch (IOException e) { e.printStackTrace(); } } } public static void main(String[] args) { FileCopyRunner noBufferStreamCopy = new FileCopyRunner() { @Override public void copyFile(File sourse, File target) { InputStream fin = null; OutputStream fout = null; try { fin = new FileInputStream(sourse); fout = new FileOutputStream(target); int result; while((result = fin.read()) != -1){ fout.write(result); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ close(fin); close(fout); } } @Override public String toString() { return "noBufferStreamCopy"; } }; FileCopyRunner bufferedStreamCopy = new FileCopyRunner() { @Override public void copyFile(File sourse, File target) { InputStream fin = null; OutputStream fout = null; try { fin = new BufferedInputStream(new FileInputStream(sourse)); fout = new BufferedOutputStream(new FileOutputStream(target)); byte[] buffer = new byte[8192]; int result; while((result = fin.read(buffer)) != -1){ fout.write(buffer , 0 ,result); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ close(fin); close(fout); } } @Override public String toString() { return "bufferedStreamCopy"; } }; FileCopyRunner nioBufferCopy = new FileCopyRunner() { @Override public void copyFile(File sourse, File target) { FileChannel fin = null; FileChannel fout = null; try { fin = new FileInputStream(sourse).getChannel(); fout = new FileOutputStream(target).getChannel(); ByteBuffer buffer = ByteBuffer.allocate(8192); while(fin.read(buffer) != -1){ buffer.flip(); //开始读模式 while(buffer.hasRemaining()){ fout.write(buffer); } buffer.clear(); // 开始写模式 } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ close(fin); close(fout); } } @Override public String toString() { return "nioBufferCopy"; } }; FileCopyRunner nioTransferCopy = new FileCopyRunner() { @Override public void copyFile(File sourse, File target) { FileChannel fin = null; FileChannel fout = null; try { fin = new FileInputStream(sourse).getChannel(); fout = new FileOutputStream(target).getChannel(); long transferred = 0; long size = fin.size(); while(transferred != size){ transferred += fin.transferTo(0,size,fout); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ close(fin); close(fout); } } @Override public String toString() { return "nioTransferCopy"; } }; File one = new File("E:\\test\\1.png"); File oneCopy = new File("E:\\test\\1-copy.png"); System.out.println("---Copying one---"); benchmark(noBufferStreamCopy , one , oneCopy); benchmark(bufferedStreamCopy , one , oneCopy); benchmark(nioBufferCopy , one , oneCopy); benchmark(nioTransferCopy , one , oneCopy); File two = new File("E:\\test\\2.mp4"); File twoCopy = new File("E:\\test\\2-copy.mp4"); System.out.println("---Copying two---"); // benchmark(noBufferStreamCopy , two , twoCopy); benchmark(bufferedStreamCopy , two , twoCopy); benchmark(nioBufferCopy , two , twoCopy); benchmark(nioTransferCopy , two , twoCopy); File three = new File("E:\\test\\3.mp4"); File threeCopy = new File("E:\\test\\3-copy.mp4"); System.out.println("---Copying three---"); // benchmark(noBufferStreamCopy , three , threeCopy); benchmark(bufferedStreamCopy , three , threeCopy); benchmark(nioBufferCopy , three , threeCopy); benchmark(nioTransferCopy , three , threeCopy); File four = new File("E:\\test\\4.avi"); File fourCopy = new File("E:\\test\\4-copy.avi"); System.out.println("---Copying four---"); // benchmark(noBufferStreamCopy , four , fourCopy); benchmark(bufferedStreamCopy , four , fourCopy); benchmark(nioBufferCopy , four , fourCopy); benchmark(nioTransferCopy , four , fourCopy); } }
匿名内部类一:网络
使用FileInputStream
、FileOutputStream
来Copy文件,它是一个字节一个字节进行read
的,因此也是一个字节一个字节进行write
的,read()
的源码注释很清楚的写出来了,因此这种方法的性能特别差,等下用较大文件测试时,咱们选择跳过这种方法(由于过久了),内部逻辑应该很简单吧,从read()
的源码注释能够知道read()
的返回值是介于0-255
的值,其实就是读取的一个字节(8位)The value byte is returned as an int in the range 0 to 255
。ide
* Reads the next byte of data from the input stream. The value byte is * returned as an <code>int</code> in the range <code>0</code> to * <code>255</code>. If no byte is available because the end of the stream * has been reached, the value <code>-1</code> is returned. This method * blocks until input data is available, the end of the stream is detected, * or an exception is thrown.
FileCopyRunner noBufferStreamCopy = new FileCopyRunner() { @Override public void copyFile(File source, File target) { InputStream fin = null; OutputStream fout = null; try { fin = new FileInputStream(source); fout = new FileOutputStream(target); int result; while((result = fin.read()) != -1){ fout.write(result); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ close(fin); close(fout); } } @Override public String toString() { return "noBufferStreamCopy"; } };
匿名内部类二:svg
第二种方法使用BufferedInputStream
、BufferedOutputStream
来进行文件的Copy,它们会产生一个缓冲区,默认大小都是8192字节
,源码以下:性能
private static int DEFAULT_BUFFER_SIZE = 8192; /** * Creates a <code>BufferedInputStream</code> * and saves its argument, the input stream * <code>in</code>, for later use. An internal * buffer array is created and stored in <code>buf</code>. * * @param in the underlying input stream. */ public BufferedInputStream(InputStream in) { this(in, DEFAULT_BUFFER_SIZE); }
/** * Creates a new buffered output stream to write data to the * specified underlying output stream. * * @param out the underlying output stream. */ public BufferedOutputStream(OutputStream out) { this(out, 8192); }
利用缓冲区,会大大提高性能,由于避免了频繁的打开、关闭文件,有了缓冲区,咱们每次对文件进行读、写操做,均可以读、写更多字节数据,减小了打开、关闭文件等操做的次数。测试
FileCopyRunner bufferedStreamCopy = new FileCopyRunner() { @Override public void copyFile(File source, File target) { InputStream fin = null; OutputStream fout = null; try { fin = new BufferedInputStream(new FileInputStream(source)); fout = new BufferedOutputStream(new FileOutputStream(target)); byte[] buffer = new byte[8192]; int result; while((result = fin.read(buffer)) != -1){ fout.write(buffer , 0 ,result); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ close(fin); close(fout); } } @Override public String toString() { return "bufferedStreamCopy"; } };
匿名内部类三:this
第三种方法使用NIO
中的Channel
、Buffer
来进行Copy文件。
Java网络编程-NIO原理spa
和上一种方法同样,这里建立8192字节
的Buffer
,方便进行性能的对比。
ByteBuffer buffer = ByteBuffer.allocate(8192);
下面两种操做,你们应该知道吧,不知道就往下看。
buffer.flip(); //开始读模式
buffer.clear(); // 开始写模式
源码以下(先无论mark
有什么用):
private int position = 0; private int limit; private int capacity; public final Buffer flip() { limit = position; position = 0; mark = -1; return this; } public final Buffer clear() { position = 0; limit = capacity; mark = -1; return this; }
下面这张图应该描述的很清楚。
position
位置以前的数据都是写入的(包括position
位置),每写入一字节数据,position++
,因此clear()
将position
置0
,是否是就说明开始准备要写入了,而且limit = capacity
,即进入写模式。0
位置到position
位置的数据都是写入的,调用flip()
,将limit
置成position
,而position
置成0
,因此position(0)
到limit(原position位置)
是否是就是要被读取的数据范围,即进入读模式。FileCopyRunner nioBufferCopy = new FileCopyRunner() { @Override public void copyFile(File source, File target) { FileChannel fin = null; FileChannel fout = null; try { fin = new FileInputStream(source).getChannel(); fout = new FileOutputStream(target).getChannel(); ByteBuffer buffer = ByteBuffer.allocate(8192); while(fin.read(buffer) != -1){ buffer.flip(); //开始读模式 while(buffer.hasRemaining()){ fout.write(buffer); } buffer.clear(); // 开始写模式 } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ close(fin); close(fout); } } @Override public String toString() { return "nioBufferCopy"; } };
匿名内部类四:
第四种方法只使用NIO
的Channel
来进行文件的Copy,Channel
经过transferTo()
能够把数据写入另外一个Channel
。
从transferTo()
的源码注释也能够看出。
Transfers bytes from this channel’s file to the given writable byte channel.
FileCopyRunner nioTransferCopy = new FileCopyRunner() { @Override public void copyFile(File source, File target) { FileChannel fin = null; FileChannel fout = null; try { fin = new FileInputStream(source).getChannel(); fout = new FileOutputStream(target).getChannel(); long transferred = 0; long size = fin.size(); while(transferred != size){ transferred += fin.transferTo(0,size,fout); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ close(fin); close(fout); } } @Override public String toString() { return "nioTransferCopy"; } };
性能对比
文件详情以下:
每种方法Copy文件5
次,计算平均耗时(第一种方法除外,由于它太慢了)。
输出:
---Copying one--- noBufferStreamCopy:143.4 bufferedStreamCopy:0.4 nioBufferCopy:1.0 nioTransferCopy:0.4 ---Copying two--- bufferedStreamCopy:5.0 nioBufferCopy:5.8 nioTransferCopy:2.6 ---Copying three--- bufferedStreamCopy:161.0 nioBufferCopy:154.0 nioTransferCopy:90.8 ---Copying four--- bufferedStreamCopy:1659.0 nioBufferCopy:1294.4 nioTransferCopy:1266.6
我测试了不少次,须要缓冲区的方法,性能跟文件大小、缓冲区大小都有关系。
不过,第四种方法性能仍是挺不错的。
若是有说错的地方,请你们不吝赐教(记得留言哦~~~~)。