Java IO实现文件拷贝,(无缓冲区、带缓冲区、带有缓冲区的channel、无缓冲区的Channel拷贝)对比
程序员文章站
2022-05-23 08:36:35
...
主要拷贝细节:
此文主要通过四种不同的拷贝方式比较各自的效率:
无缓冲字节流拷贝: 每次拷贝一字节,直到拷贝完毕
int res;
while ((res = fin.read()) != -1) {
fout.write(res);
}
带缓存字节流拷贝: 每次拷贝1024字节,直到拷贝完毕
byte[] bytes = new byte[1024];
//每次读取小于等于1024字节的数据进行拷贝
while ( fin.read(bytes) != -1) {
fout.write(bytes,0,bytes.length);
}
带有缓冲区的channel拷贝: 先从fin写到buffer,fout再把fuffer写到目标文件;其中buffer有读写状态转换
ByteBuffer buffer = ByteBuffer.allocate(1024);
//从channel管道读到写到buffer缓存字节流
while((fin.read(buffer)) != -1) {
buffer.flip(); //buffer状态从写转为读
//一次不一定会读完缓冲区buffer
while (buffer.hasRemaining()) {
fout.write(buffer);
}
buffer.clear(); //buffer状态从读转为写
}
不带缓冲区的channel拷贝: 循环从channel管道拷贝到channel管道
long sourceSize = fin.size();
long copy = 0L;
//不一定一次能完成拷贝,循环进行拷贝
while (copy < sourceSize) {
copy += fin.transferTo(0, sourceSize, fout);
}
具体代码实现:
定义接口:FileCopyRunner
package com.xiaojie.net.nio;
import java.io.File;
/**
* @author Mrli
* @date 2020/9/28 17:12
*/
public interface FileCopyRunner {
/**
* 文件拷贝接口
* @param source
* @param target
*/
public void copyFile(File source, File target);
}
定义实现类兼测试:FileCopyDemo
package com.xiaojie.net.nio;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* @author Mrli
* @date 2020/9/28 17:11
*/
public class FileCopyDemo {
/**
* 拷贝次数,用来测试各方法平均速度
*/
private static final int NUM = 5;
/**
* 不使用任何缓冲的流拷贝,每次读取一字节拷贝一字节
*/
private static 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 res;
while ((res = fin.read()) != -1) {
fout.write(res);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
close(fin);
close(fout);
}
}
@Override
public String toString() {
return "noBufferStreamCopy";
}
};
/**
* 使用缓冲区的流的拷贝,每次读取1024字节缓冲流,从缓存流拷贝
*/
private static FileCopyRunner bufferStreamCopy = 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);
byte[] bytes = new byte[1024];
//每次读取小于等于1024字节的数据进行拷贝
while ( fin.read(bytes) != -1) {
fout.write(bytes,0,bytes.length);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "bufferStreamCopy";
}
};
/**
* 使用带有缓冲区的channel拷贝, nio
*/
private static 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(1024);
//从channel管道读到写到buffer缓存字节流
while((fin.read(buffer)) != -1) {
buffer.flip(); //buffer状态从写转为读
//一次不一定会读完缓冲区buffer
while (buffer.hasRemaining()) {
fout.write(buffer);
}
buffer.clear(); //buffer状态从读转为写
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "nioBufferCopy";
}
};
/**
* 使用没有缓冲区的channel拷贝文件
*/
private static 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 sourceSize = fin.size();
long copy = 0L;
//不一定一次能完成拷贝,循环进行拷贝
while (copy < sourceSize) {
copy += fin.transferTo(0, sourceSize, fout);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "nioTransferCopy";
}
};
/**
* 关闭文件流
* @param closeable
*/
private static void close(Closeable closeable){
if(closeable != null) {
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 测试文件拷贝的时间花费
* @param fileCopyRunner
* @param source
* @param target
*/
public static void benchMark(FileCopyRunner fileCopyRunner, File source, File target) {
long elapsed = 0L;
for (int i = 0; i < NUM; i++) {
long startTime = System.currentTimeMillis();
fileCopyRunner.copyFile(source,target);
elapsed += System.currentTimeMillis() - startTime;
target.delete();
}
System.out.println(fileCopyRunner + ":" + elapsed/NUM);
}
/**
* 主函数,运行入口
* @param args
*/
public static void main(String[] args) {
File smallFile = new File("E:\\学习视频\\2020后端路线.png");
File smallFileCopy = new File("E:\\学习视频\\copy.png");
System.out.println("---------500kB file copy 5次-------------");
benchMark(noBufferStreamCopy,smallFile,smallFileCopy);
benchMark(bufferStreamCopy,smallFile,smallFileCopy);
benchMark(nioBufferCopy,smallFile,smallFileCopy);
benchMark(nioTransferCopy,smallFile,smallFileCopy);
File bigFile = new File("E:\\学习视频\\apache-tomcat-9.0.30.tar.gz");
File bigFileCopy = new File("E:\\学习视频\\copy.gz");
System.out.println("---------10MB file copy 5次-------------");
benchMark(noBufferStreamCopy,bigFile,bigFileCopy);
benchMark(bufferStreamCopy,bigFile,bigFileCopy);
benchMark(nioBufferCopy,bigFile,bigFileCopy);
benchMark(nioTransferCopy,bigFile,bigFileCopy);
}
}
测试结果:
对比发现:
在小文件与大文件拷贝中
1、带缓冲字节流拷贝比不带缓冲字节流拷贝 快 800 - 1000倍 左右
体现: 4901/6 = 816 109066/119 = 916
2、不带缓冲的Channel NIO 拷贝方式,比任意一种拷贝都快
体现: 0 < 6 < 10 < 4901
3、带缓冲的字节流拷贝 与 带缓冲流的Channel NIO 拷贝方式,效率接近,但还是前者优于后者
体现: (6,10) , (119,124)
Trust yourself. Create the kind of self that you will be happy to live with all your life.
相信自己。创造一种自我,让您终生乐于生活。
上一篇: Java 基于BIO实现 Client与Server通信
下一篇: Java Web之过滤器