FileChannel管道流复制文件是基于nio的传输方式。速度上有30%的提升。其次在我的项目中使用传统FileOutputStream方式,在复制大文件时。进度打印出现迟滞。综合这两点选择使用FileChannel方案。
public class CopyPasteUtil {
private static long dirSize = 0;// 文件夹总体积
private static long hasReadSize = 0;// 已复制的部分,体积
private static CopyProgressDialog progressDialog;// 进度提示框
private static Thread copyFileThread;
private static Runnable run = null;
private static FileInputStream fileInputStream = null;
private static FileOutputStream fileOutputStream = null;
private static FileChannel fileChannelOutput = null;
private static FileChannel fileChannelInput = null;
private static Thread copyDirThread;
private static BufferedInputStream inbuff = null;
private static BufferedOutputStream outbuff = null;
/**
* handler用于在主线程刷新ui
*/
private final static Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == 0) {
int progress = msg.getData().getInt("progress");
long fileVolume = msg.getData().getLong("fileVolume");
progressDialog.setProgress(progress);
progressDialog.setProgressText(progress + "%");
progressDialog.setFileVolumeText(fileVolume * progress / 100 + " MB/" + fileVolume + " MB");
}else if(msg.what==1){
if(progressDialog != null) {
int fileVolume = msg.getData().getInt("fileVolume");
progressDialog.setFileVolumeText(0 + " MB/" + fileVolume + " MB");
}
}
};
};
/**
* 复制单个文件
*/
public static boolean copyFile(final String oldPathName, final String newPathName, Context context) {
//大于50M时,才显示进度框
final File oldFile = new File(oldPathName);
if (oldFile.length() > 50 * 1024 * 1024) {
progressDialog = new CopyProgressDialog(context);
progressDialog.show();
progressDialog.setNameText(oldPathName);
progressDialog.setOnCancelListener(new OnCancelListener() {//点击返回取消时,关闭线程和流
@Override
public void onCancel(DialogInterface arg0) {
run = null;
copyFileThread.interrupt();
copyFileThread = null;
try {
fileInputStream.close();
fileOutputStream.close();
fileChannelOutput.close();
fileChannelInput.close();
} catch (IOException e) {
Log.e("CopyPasteUtil", "CopyPasteUtil copyFile error:" + e.getMessage());
}
}
});
}
run = new Runnable() {
@Override
public void run() {
try {
File fromFile = new File(oldPathName);
File targetFile = new File(newPathName);
fileInputStream = new FileInputStream(fromFile);
fileOutputStream = new FileOutputStream(targetFile);
fileChannelOutput = fileOutputStream.getChannel();
fileChannelInput = fileInputStream.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(4096);
long transferSize = 0;
long size = new File(oldPathName).length();
int fileVolume = (int) (size / 1024 /1024);
int tempP = 0;
int progress = 0;
while (fileChannelInput.read(buffer) != -1) {
buffer.flip();
transferSize += fileChannelOutput.write(buffer);
progress = (int) (transferSize * 100 / size);
if(progress>tempP){
tempP = progress;
Message message = handler.obtainMessage(0);
Bundle b = new Bundle();
b.putInt("progress", progress);
b.putLong("fileVolume", fileVolume);
message.setData(b);
handler.sendMessage(message);
}
buffer.clear();
}
fileOutputStream.flush();
fileOutputStream.close();
fileInputStream.close();
fileChannelOutput.close();
fileChannelInput.close();
if(progressDialog.isShow()){
progressDialog.dismiss();
}
} catch (Exception e) {
Log.e("CopyPasteUtil", "CopyPasteUtil copyFile error:" + e.getMessage());
}
}
};
copyFileThread = new Thread(run);
copyFileThread.start();
return true;
}
/**
* 复制文件夹
*/
public static void copyDirectiory(final String sourceDir, final String targetDir, final Context context) {
if (context != null) {
if (dirSize > 50 * 1024 * 1024) {
progressDialog = new CopyProgressDialog(context);
progressDialog.show();
progressDialog.setNameText(sourceDir);
progressDialog.setOnCancelListener(new OnCancelListener() {//点击返回取消时,关闭线程和流
@Override
public void onCancel(DialogInterface arg0) {
run = null;
copyDirThread.interrupt();
copyDirThread = null;
try {
if(fileInputStream != null) fileInputStream.close();
if(fileOutputStream != null) fileOutputStream.close();
if(inbuff != null) inbuff.close();
if(outbuff != null) outbuff.close();
if(fileChannelOutput != null) fileChannelOutput.close();
if(fileChannelInput != null) fileChannelInput.close();
} catch (IOException e) {
Log.e("CopyPasteUtil", "CopyPasteUtil copyDirectiory error:" + e.getMessage());
}
}
});
}
}
run = new Runnable() {
@Override
public void run() {
(new File(targetDir)).mkdirs();
File[] file = (new File(sourceDir)).listFiles();// 获取源文件夹当下的文件或目录
for (int i = 0; i < file.length; i++) {
if (file[i].isFile()) {
File sourceFile = file[i];
File targetFile = new File(
new File(targetDir).getAbsolutePath() + File.separator + file[i].getName());// 目标文件
copyFile(sourceFile, targetFile);
}
if (file[i].isDirectory()) {
String dir1 = sourceDir + "/" + file[i].getName();
String dir2 = targetDir + "/" + file[i].getName();
copyDirectiory(dir1, dir2, null);
}
}
}
};
copyDirThread = new Thread(run);
copyDirThread.start();
}
/**
* 复制单个文件,用于上面的复制文件夹方法
*
* @param sourcefile
* 源文件路径
* @param targetFile
* 目标路径
*/
public static synchronized void copyFile(final File sourcefile, final File targetFile) {
try {
fileInputStream = new FileInputStream(sourcefile);
inbuff = new BufferedInputStream(fileInputStream);
fileOutputStream = new FileOutputStream(targetFile);// 新建文件输出流并对它进行缓冲
outbuff = new BufferedOutputStream(fileOutputStream);
int fileVolume = (int) (dirSize / (1024 * 1024));
fileChannelOutput = fileOutputStream.getChannel();
fileChannelInput = fileInputStream.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(4096);
long transferSize = 0;
int tempP = 0;
int progress = 0;
while (fileChannelInput.read(buffer) != -1) {
buffer.flip();
transferSize += fileChannelOutput.write(buffer);
if (dirSize > 50 * 1024 * 1024) {
progress = (int) (((transferSize + hasReadSize) * 100) / dirSize);
if(progress>tempP){
tempP = progress;
Message message = handler.obtainMessage(0);
Bundle b = new Bundle();
b.putInt("progress", progress);
b.putLong("fileVolume", fileVolume);
message.setData(b);
handler.sendMessage(message);
if(progressDialog.isShow() && progress==100){
progressDialog.dismiss();
}
}
}
buffer.clear();
}
hasReadSize += sourcefile.length();
outbuff.flush();
inbuff.close();
outbuff.close();
fileOutputStream.close();
fileInputStream.close();
fileChannelOutput.close();
fileChannelInput.close();
} catch (FileNotFoundException e) {
Log.e("CopyPasteUtil", "CopyPasteUtil copyFile error:" + e.getMessage());
} catch (IOException e) {
Log.e("CopyPasteUtil", "CopyPasteUtil copyFile error:" + e.getMessage());
}
}
/**
* 获取文件夹大小
* @param file
*/
public static void getDirSize(File file) {
if (file.isFile()) {
// 如果是文件,获取文件大小累加
dirSize += file.length();
} else if (file.isDirectory()) {
File[] f1 = file.listFiles();
for (int i = 0; i < f1.length; i++) {
// 调用递归遍历f1数组中的每一个对象
getDirSize(f1[i]);
}
}
}
/**
* 初始化全局变量
*/
public static void initDirSize() {
dirSize = 0;
hasReadSize = 0;
}
/**
* 复制文件夹前,初始化两个变量
*/
public static void initValueAndGetDirSize(File file) {
initDirSize();
getDirSize(file);
}
}
在使用toChannel.transferFrom(fromChannel, 0, Long.MAX_VALUE);这个方法时,遇到一个问题就是,在jdk1.7环境(android7之前)会出现复制文件失败,报warning。
原来是Long.MAX_VALUE在jdk7中被执行为Integer.MAX_VALUE导致长度不够。真是一波三折啊。最终解决方式是通过一篇博客,https://fucknmb.com/2017/11/06/Android-FileChannel%E7%9A%84%E5%9D%91/。感谢!记录下,感觉生僻的技术还是得谷歌。积累吧!
网友评论