1、bio拷贝文件
/**
* bio 拷贝文件
* @param mFile 资源文件
* @param toFile 目的地
* @throws Exception
*/
private static void c2(File mFile, File toFile) throws Exception {
FileInputStream inputStream = new FileInputStream(mFile);
FileOutputStream outputStream = new FileOutputStream(toFile);
byte[] bt = new byte[1024];
int length = 0;
while ((length = inputStream.read(bt)) != -1) {
outputStream.write(bt, 0, length);
}
outputStream.flush();
outputStream.close();
inputStream.close();
}
2、nio拷贝文件
/**
* nio 拷贝文件
* @param mFile 资源文件
* @param toFile 目的地
* @throws Exception
*/
private static void c1(File mFile, File toFile) {
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
FileChannel inputChannel = null;
FileChannel outChannel = null;
try {
fileInputStream = new FileInputStream(mFile);
fileOutputStream = new FileOutputStream(toFile);
//获取管道
inputChannel = fileInputStream.getChannel();
outChannel = fileOutputStream.getChannel();
//开始拷贝文件
//3个参数意思是: 从第0个位置开始、到inputChannel.size()位置结束、文件目标位置
inputChannel.transferTo(0, inputChannel.size(), outChannel);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fileOutputStream.close();
fileInputStream.close();
inputChannel.close();
outChannel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、多线程拷贝文件
3.1 我们新建一个类,继承Thread用于处理数据
public class CopyThread extends Thread {
private String sourcePath;
private String putPath;
private long start, end;
private FileChannel outChannel;
private FileChannel inChannel;
private RandomAccessFile input;
private RandomAccessFile output;
/**
* 多线程拷贝文件
*
* @param sourcePath 源文件路径
* @param putPath 目标路径
* @param start 从第几个位置开始写入
* @param end 到那个位置结束
*/
public CopyThread(String sourcePath, String putPath, long start, long end) {
this.sourcePath = sourcePath;
this.putPath = putPath;
this.end = end;
this.start = start;
}
@Override
public void run() {
try {
long starttime = System.currentTimeMillis();
input = new RandomAccessFile(sourcePath, "r");
output = new RandomAccessFile(putPath, "rw");
//这个很关键,定位文件指针的起始位置
input.seek(start);
output.seek(start);
inChannel = input.getChannel();
outChannel = output.getChannel();
//拷贝文件
inChannel.transferTo(start, (end - start), outChannel);
System.out.println(Thread.currentThread().getName() + "结束用时:" + (System.currentTimeMillis() - starttime));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (outChannel != null) {
outChannel.close();
}
if (inChannel != null) {
inChannel.close();
}
if (output != null) {
output.close();
}
if (input != null) {
input.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
写一个方法来测试多线程拷贝文件
/**
* 多线程拷贝文件
* @param mFile 资源文件
* @param toFile 目的地
* @throws Exception
*/
private static void copy(File mFile, File toFile) {
//把一个文件分成几块,就是要开几个线程
private static int COUNT_THREAD = 5;
String inpath = mFile.getAbsolutePath();
String outpath = toFile.getAbsolutePath();
//文件大小
long len = mFile.length();
//计算一个线程 里面有多少数据需要拷贝
long oneNum = len / COUNT_THREAD;
//例如 假设文件大小是 11, 11/5
for (int i = 0; i < COUNT_THREAD - 1; i++) {
//文件分块拷贝数据
new CopyThread
(inpath, outpath,
oneNum * i, oneNum * (i + 1)).start();
}
//整除的时候不一定能除干净,所以还需要做一个取模运算
new CopyThread(inpath, outpath,
oneNum + (len % COUNT_THREAD), len).start();
System.out.println("拷贝结束");
}
到这里FileChannel多线程拷贝文件就已经完成了。但是并不完美,执行代码后会发现立马输出了 “拷贝结束” ,我想要的需求是当文件拷贝完成后才输出这句话,看3.2。
3.2 为了实现多线程文件拷贝同步,我们需要做一点小小点改变, 新建一个类,实现Runnable接口 ,构造参数比之前多了一个 CountDownLatch
public class CopyRunnable implements Runnable {
private String sourcePath;
private String putPath;
private long start, end;
private FileChannel outChannel;
private FileChannel inChannel;
private CountDownLatch downLatch;
private RandomAccessFile input;
private RandomAccessFile output;
/**
* 多线程拷贝文件(同步)
*
* @param sourcePath 源文件路径
* @param putPath 目标路径
* @param start 从第几个位置开始写入
* @param end 到那个位置结束
* @param downLatch 同步控制器
*/
public CopyRunnable(String sourcePath, String putPath, long start, long end, CountDownLatch downLatch) {
this.sourcePath = sourcePath;
this.putPath = putPath;
this.end = end;
this.start = start;
this.downLatch = downLatch;
}
@Override
public void run() {
try {
input = new RandomAccessFile(sourcePath, "r");
output = new RandomAccessFile(putPath, "rw");
input.seek(start);
output.seek(start);
inChannel = input.getChannel();
outChannel = output.getChannel();
copy();
} catch (Exception e) {
e.printStackTrace();
} finally {
//每个线程结束 调用此方法来计数 减一
downLatch.countDown();
try {
if (outChannel != null) {
outChannel.close();
}
if (inChannel != null) {
inChannel.close();
}
if (output != null) {
output.close();
}
if (input != null) {
input.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private synchronized void copy() throws IOException {
inChannel.transferTo(start, (end - start), outChannel);
}
}
3.4 测试多线程拷贝文件
/**
* 多线程拷贝文件 同步
* @param mFile 资源文件
* @param toFile 目的地
* @throws Exception
*/
private static void copy(File mFile, File toFile) {
private static int COUNT_THREAD = 5;
String inpath = mFile.getAbsolutePath();
String outpath = toFile.getAbsolutePath();
long len = mFile.length();
long oneNum = len / COUNT_THREAD;
//我们这里使用了线程池 核心线程、最大线程数、等待时间 都可以自己的实际需要做修改
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(COUNT_THREAD, COUNT_THREAD, 2, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
//线程同步工具,设置一个线程数
CountDownLatch downLatch = new CountDownLatch(COUNT_THREAD);
for (int i = 0; i < COUNT_THREAD - 1; i++) {
CopyRunnable runnable = new CopyRunnable(inpath, outpath, oneNum * i, oneNum * (i + 1), downLatch);
threadPoolExecutor.execute(runnable);
}
CopyRunnable runnable = new CopyRunnable(inpath, outpath, oneNum + (len % COUNT_THREAD), len, downLatch);
threadPoolExecutor.execute(runnable);
//线程池不再接受新的任务
threadPoolExecutor.shutdown();
System.out.println("等待文件拷贝结束");
try {
//等待,一直到downLatch中到数据为0
downLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("文件拷贝结束了");
}