CopyPasteUtil文件复制黏贴工具类
2019-11-02 本文已影响0人
颤抖的闪电
概述:总结了几篇大牛的文章,左拿右拿,东改西改,取其精华,逐步完善,撸了一个直接能用的文件搬迁的工具类,旨在于易用,易懂,简单,纤瘦。
FileChannel管道流复制文件是基于nio的传输方式。速度上有30%的提升,传统FileOutputStream方式,在复制大文件时。进度打印出现迟滞。综合这两点选择使用FileChannel方案。
该工具类主要用于复制文件、文件夹(批量文件)
以下内容主要包括:
- 主要核心代码
- 主要使用方式
主要核心代码:
import android.content.Context;
import android.content.DialogInterface;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class CopyPasteUtil {
/**
* copy过程的监听接口
*/
public interface CopyPasteListener {
void onSuccess();
void onProgress(long dirFileCount, long hasReadCount, long dirSize, long hasReadSize);
void onFail(String errorMsg);
void onCancle();
}
/**
* 初始化的监听接口
*/
public interface InitListener {
void onNext(long dirFileCount, long dirSize, CopyPasteImp imp);
}
public static CopyPasteImp build() {
return new CopyPasteImp();
}
public static class CopyPasteImp {
private long dirSize = 0;// 文件夹总体积
private long hasReadSize = 0;// 已复制的部分,体积
private long dirFileCount = 0;// 文件总个数
private long hasReadCount = 0;// 已复制的文件个数
private CommonProgressDialog progressDialog;// 进度提示框
private boolean isNeesDefaulProgressDialog = true;
private Thread copyFileThread;
private FileInputStream fileInputStream = null;
private FileOutputStream fileOutputStream = null;
private FileChannel fileChannelOutput = null;
private FileChannel fileChannelInput = null;
private BufferedInputStream inbuff = null; //todo 屏蔽之后,代码照跑无误,严重怀疑buff是否还有作用,未针对调试。
private BufferedOutputStream outbuff = null;
/**
* 复制单个文件
*/
public boolean copyFile(final String oldPathName, final String newPathName, Context context) {
//大于50M时,才显示进度框
final File oldFile = new File(oldPathName);
if (oldFile.length() > 50 * 1024 * 1024) {
if (isNeesDefaulProgressDialog && null == progressDialog) {
progressDialog = new CommonProgressDialog(context);
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
// TODO Auto-generated method stub
copyFileThread.interrupt();
copyFileThread = null;
try {
fileInputStream.close();
fileOutputStream.close();
fileChannelOutput.close();
fileChannelInput.close();
} catch (IOException e) {
Log.e("CopyPasteUtil", "CopyPasteUtil copyFile error:" + e.getMessage());
}
}
});
progressDialog.show();
}
}
Runnable 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;
if (null != progressDialog) {
progressDialog.setMax(fileVolume * 1024 * 1024);
}
while (fileChannelInput.read(buffer) != -1) {
buffer.flip();
transferSize += fileChannelOutput.write(buffer);
progress = (int) transferSize / (1024 * 1024);
if (progress > tempP) {
tempP = progress;
if (null != progressDialog) {
progressDialog.setProgress(progress * 1024 * 1024);
}
}
buffer.clear();
}
fileOutputStream.flush();
fileOutputStream.close();
fileInputStream.close();
fileChannelOutput.close();
fileChannelInput.close();
if (null != progressDialog && progressDialog.isShowing()) {
progressDialog.dismiss();
}
} catch (Exception e) {
Log.e("CopyPasteUtil", "CopyPasteUtil copyFile error:" + e.getMessage());
}
}
};
copyFileThread = new Thread(run);
copyFileThread.start();
return true;
}
/**
* 复制文件夹
*/
public void copyDirectiory(Context context, String sourceDir, String targetDir, CopyPasteListener call) {
if (context != null) {
if (isNeesDefaulProgressDialog && null == progressDialog) {
progressDialog = new CommonProgressDialog(context);
}
if (null != progressDialog) {
progressDialog.setMessage("文件迁移正在进行中...");
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
// TODO Auto-generated method stub
copyFileThread.interrupt();
copyFileThread = null;
try {
if (null != fileInputStream) fileInputStream.close();
if (null != fileOutputStream) fileOutputStream.close();
if (null != inbuff) inbuff.close();
if (null != outbuff) outbuff.close();
if (null != fileChannelOutput) fileChannelOutput.close();
if (null != fileChannelInput) fileChannelInput.close();
if (null != call) {
call.onCancle();
}
} catch (IOException e) {
Log.e("CopyPasteUtil", "CopyPasteUtil copyDirectiory error:" + e.getMessage());
}
}
});
progressDialog.show();
}
}
Runnable run = new Runnable() {
@Override
public void run() {
copyDirMethod(0, sourceDir, targetDir, call);
}
};
copyFileThread = new Thread(run);
copyFileThread.start();
}
/**
* 复制文件夹,真正的执行动作
*/
private void copyDirMethod(int dirLevel, String sourceDir, String targetDir, CopyPasteListener call) {
(new File(targetDir)).mkdirs();
dirLevel++; //进入下一层 层级+1
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());// 目标文件
copyFileMethod(sourceFile, targetFile, call);
} else if (file[i].isDirectory()) {
String dir1 = sourceDir + "/" + file[i].getName();
String dir2 = targetDir + "/" + file[i].getName();
copyDirMethod(dirLevel, dir1, dir2, call);
}
}
dirLevel--;//该层已经循环遍历完毕,返回上一层 层级-1
//层级小于等于0,说明已经计算完毕,递归回到最顶层
if (dirLevel <= 0 && null != call) {
if (null != progressDialog) {
progressDialog.setMessage("文件迁移完成");
call.onSuccess();
}
}
}
/**
* 复制单个文件,用于上面的复制文件夹方法
*
* @param sourcefile 源文件路径
* @param targetFile 目标路径
*/
private synchronized void copyFileMethod(final File sourcefile, final File targetFile, CopyPasteListener call) {
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;
if (null != progressDialog) {
progressDialog.setMax(fileVolume * 1024 * 1024);
}
while (fileChannelInput.read(buffer) != -1) {
buffer.flip();
transferSize += fileChannelOutput.write(buffer);
progress = (int) (transferSize + hasReadSize) / (1024 * 1024);
if (progress > tempP) {
tempP = progress;
if (null != progressDialog) {
progressDialog.setProgress(progress * 1024 * 1024);
}
if (null != call) { //此处重点在于传递大小
call.onProgress(dirFileCount, hasReadCount, dirSize, transferSize + hasReadSize);
}
}
buffer.clear();
}
hasReadCount++;
hasReadSize += sourcefile.length();
if (null != call) { //此处重点在于传递文件个数
call.onProgress(dirFileCount, hasReadCount, dirSize, hasReadSize);
}
outbuff.flush();
fileOutputStream.flush();
inbuff.close();
outbuff.close();
fileOutputStream.close();
fileInputStream.close();
fileChannelOutput.close();
fileChannelInput.close();
// if (hasReadSize >= dirSize && null != call) {
// call.onSuccess();
// }
} catch (FileNotFoundException e) {
if (null != call) {
call.onFail(e.getMessage());
}
} catch (IOException e) {
if (null != call) {
call.onFail(e.getMessage());
}
}
}
/**
* 删除整个文件夹
* FileCopeTool.deleteFolder(URL.HtmlPath + "/" + identify);
*
* @param path 路径,无需文件名
*/
public void deleteFolder(String path) {
File f = new File(path);
if (f.exists()) {
// 在判断它是不是一个目录
if (f.isDirectory()) {
// 列出该文件夹下的所有内容
String[] fileList = f.list();
if (fileList == null) {
return;
}
for (int i = 0; i < fileList.length; i++) {
// 对每个文件名进行判断
// 如果是文件夹 那么就循环deleteFolder
// 如果不是,直接删除文件
String name = path + File.separator + fileList[i];
File ff = new File(name);
if (ff.isDirectory()) {
deleteFolder(name);
} else {
ff.delete();
}
}
// 最后删除文件夹
f.delete();
} else {
// 该文件夹不是一个目录
}
} else {
//不存在该文件夹
}
}
/**
* 获取文件夹大小
*
* @param dirLevel 计算文件夹的层级,用于判断递归遍历是否完成,初始调用应该设置为0
* @param file
* @param call 完成回调
*/
private void getDirSize(int dirLevel, File file, InitListener call) {
if (file.isFile()) {
// 如果是文件,获取文件大小累加
dirSize += file.length();
dirFileCount++;
} else if (file.isDirectory()) {
dirLevel++; //进入下一层 层级+1
File[] f1 = file.listFiles();
for (int i = 0; i < f1.length; i++) {
// 调用递归遍历f1数组中的每一个对象
getDirSize(dirLevel, f1[i], call);
}
dirLevel--;//该层已经循环遍历完毕,返回上一层 层级-1
}
//层级小于等于0,说明已经计算完毕,递归回到最顶层
if (dirLevel <= 0 && null != call) {
call.onNext(dirFileCount, dirSize, this);
}
}
/**
* 初始化全局变量
*/
private void initDirSize() {
dirSize = 0;
hasReadSize = 0;
dirFileCount = 0;
hasReadCount = 0;
}
/**
* 复制文件夹前,初始化四个个变量
*/
public void initValueAndGetDirSize(Context context, File file, InitListener call) {
if (isNeesDefaulProgressDialog) {
progressDialog = new CommonProgressDialog(context);
progressDialog.setMessage("准备...");
progressDialog.show();
}
new Thread(new Runnable() {
@Override
public void run() {
initDirSize();
getDirSize(0, file, call);
}
}).start();
}
/**
* 默认为true
*/
public CopyPasteImp setIsNeesDefaulProgressDialog(boolean isNeed) {
isNeesDefaulProgressDialog = isNeed;
return this;
}
}
}
进度条工具类:CommonProgressDialog.java
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.StyleSpan;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.csair.mmpmobile.R;
import java.text.NumberFormat;
public class CommonProgressDialog extends AlertDialog {
private ProgressBar mProgress;
private TextView mProgressNumber;
private TextView mProgressPercent;
private TextView mProgressMessage;
private Handler mViewUpdateHandler;
private Runnable mViewUpdateRunnable;
private int mMax;
private CharSequence mMessage;
private boolean mHasStarted;
private int mProgressVal;
private String TAG = "CommonProgressDialog";
private String mProgressNumberFormat;
private NumberFormat mProgressPercentFormat;
public CommonProgressDialog(Context context) {
super(context);
// TODO Auto-generated constructor stub
initFormats();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.common_progress_dialog);
mProgress = (ProgressBar) findViewById(R.id.progress);
mProgressNumber = (TextView) findViewById(R.id.progress_number);
mProgressPercent = (TextView) findViewById(R.id.progress_percent);
mProgressMessage = (TextView) findViewById(R.id.progress_message);
mViewUpdateRunnable = new Runnable() {
@Override
public void run() {
int progress = mProgress.getProgress();
int max = mProgress.getMax();
double dProgress = (double) progress / (double) (1024 * 1024);
double dMax = (double) max / (double) (1024 * 1024);
if (mProgressNumberFormat != null) {
String format = mProgressNumberFormat;
mProgressNumber.setText(String.format(format, dProgress, dMax));
} else {
mProgressNumber.setText("");
}
if (mProgressPercentFormat != null) {
double percent = (double) progress / (double) max;
SpannableString tmp = new SpannableString(mProgressPercentFormat.format(percent));
tmp.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
0, tmp.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mProgressPercent.setText(tmp);
} else {
mProgressPercent.setText("");
}
}
};
mViewUpdateHandler = new ViewUpdateHandler();
onProgressChanged();
if (mMessage != null) {
setMessage(mMessage);
}
if (mMax > 0) {
setMax(mMax);
}
if (mProgressVal > 0) {
setProgress(mProgressVal);
}
}
private void initFormats() {
mProgressNumberFormat = "%1.2fM/%2.2fM";
mProgressPercentFormat = NumberFormat.getPercentInstance();
mProgressPercentFormat.setMaximumFractionDigits(0);
}
private void onProgressChanged() {
mViewUpdateHandler.post(mViewUpdateRunnable);
}
public void setProgressStyle(int style) {
//mProgressStyle = style;
}
public int getMax() {
if (mProgress != null) {
return mProgress.getMax();
}
return mMax;
}
public void setMax(int max) {
if (mProgress != null) {
mProgress.setMax(max);
onProgressChanged();
} else {
mMax = max;
}
}
public void setIndeterminate(boolean indeterminate) {
if (mProgress != null) {
mProgress.setIndeterminate(indeterminate);
}
// else {
// mIndeterminate = indeterminate;
// }
}
public void setProgress(int value) {
if (mHasStarted) {
mProgress.setProgress(value);
onProgressChanged();
} else {
mProgressVal = value;
}
}
@Override
public void setMessage(CharSequence message) {
// TODO Auto-generated method stub
//super.setMessage(message);
if (mProgressMessage != null) {
mViewUpdateHandler.post(new Runnable() {
@Override
public void run() {
mProgressMessage.setText(message);
}
});
} else {
mMessage = message;
}
}
@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
mHasStarted = true;
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
mHasStarted = false;
}
static class ViewUpdateHandler extends Handler {
}
}
进度条布局文件:common_progress_dialog.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/gray"
android:orientation="vertical">
<TextView
android:id="@+id/progress_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="40dp"
android:text="Message"
android:textColor="#ffffff"
android:textSize="16dp" />
<ProgressBar
android:id="@+id/progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="15dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="40dp"
android:layout_marginRight="20dp"
android:progressDrawable="@drawable/common_progressdialog_progressbar_background" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/progress_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:layout_weight="1"
android:gravity="left"
android:text="10%"
android:textColor="#ffffff"
android:textSize="12sp" />
<TextView
android:id="@+id/progress_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="30dp"
android:layout_weight="1"
android:gravity="right"
android:text="10M/100M"
android:textColor="#ffffff"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
进度条背景资源:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<corners android:radius="4dp"/>
<gradient android:startColor="#EFF3F7"
android:endColor="#EFF3F7"/>
</shape>
</item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%">
<shape android:shape="rectangle">
<corners android:radius="4dp"/>
<gradient android:angle="45"
android:startColor="#42D673"
android:endColor="#42D673"/>
</shape>
</scale>
</item>
</layer-list>
主要使用方式
1,启用默认进度条,初始化或得总体文件大小和总个数,进入下载流程
CopyPasteUtil.build()
.setIsNeesDefaulProgressDialog(true)
.initValueAndGetDirSize(mActivity, new File(MMPUrl.SD_PATH), new CopyPasteUtil.InitListener() {
@Override
public void onNext(long dirFileCount, long dirSize, CopyPasteUtil.CopyPasteImp imp) {
int fileVolume = (int) (dirSize / (1024 * 1024));
UiThread.run(new Runnable() {
@Override
public void run() {
Toast.makeText(mActivity, "onNext->dirFileCount:" + dirFileCount + "==onNext->dirSize:" + fileVolume + "M", Toast.LENGTH_LONG).show();
}
});
imp.copyDirectiory(mActivity, MMPUrl.SD_PATH, MMPUrl.SD_PATH_COPY, new CopyPasteUtil.CopyPasteListener() {
@Override
public void onSuccess() {
UiThread.run(new Runnable() {
@Override
public void run() {
Toast.makeText(mActivity, "onSuccess:" + i++, Toast.LENGTH_LONG).show();
}
});
}
@Override
public void onProgress(long dirFileCount, long hasReadCount, long dirSize, long hasReadSize) {
UiThread.run(new Runnable() {
@Override
public void run() {
mVersion.setText(dirFileCount + "-" + hasReadCount + "==" + dirSize + "-" + hasReadSize);
}
});
}
@Override
public void onFail(String errorMsg) {
UiThread.run(new Runnable() {
@Override
public void run() {
Toast.makeText(mActivity, "onFail", Toast.LENGTH_LONG).show();
}
});
}
@Override
public void onCancle() {
UiThread.run(new Runnable() {
@Override
public void run() {
Toast.makeText(mActivity, "onCancle", Toast.LENGTH_LONG).show();
}
});
}
});
}
});
2,关闭默认进度条,直接进入下载流程,但是没有总体文件大小和总个数
CopyPasteUtil.build()
.setIsNeesDefaulProgressDialog(false)
.copyDirectiory(mActivity, MMPUrl.SD_PATH, MMPUrl.SD_PATH_COPY, new CopyPasteUtil.CopyPasteListener() {
@Override
public void onSuccess() {
UiThread.run(new Runnable() {
@Override
public void run() {
Toast.makeText(mActivity, "onSuccess:" + i++, Toast.LENGTH_LONG).show();
}
});
}
@Override
public void onProgress(long dirFileCount, long hasReadCount, long dirSize, long hasReadSize) {
UiThread.run(new Runnable() {
@Override
public void run() {
mVersion.setText(dirFileCount + "-" + hasReadCount + "==" + dirSize + "-" + hasReadSize);
}
});
}
@Override
public void onFail(String errorMsg) {
UiThread.run(new Runnable() {
@Override
public void run() {
Toast.makeText(mActivity, "onFail", Toast.LENGTH_LONG).show();
}
});
}
@Override
public void onCancle() {
UiThread.run(new Runnable() {
@Override
public void run() {
Toast.makeText(mActivity, "onCancle", Toast.LENGTH_LONG).show();
}
});
}
});
感谢:
android复制文件、文件夹,使用FileChannel带进度条
实例详解Android自定义ProgressDialog进度条对话框的实现
给progressbar设置drawable和自定义progressbar
FileCopeUtil 文件操作