android复制文件、文件夹,使用FileChannel带进度条

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/。感谢!记录下,感觉生僻的技术还是得谷歌。积累吧!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,682评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,277评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,083评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,763评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,785评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,624评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,358评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,261评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,722评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,900评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,030评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,737评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,360评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,941评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,057评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,237评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,976评论 2 355

推荐阅读更多精彩内容

  • BIO与NIO 1.传统BIO (1)特点 面向数据流 阻塞式传输 一个客户端对应一个线程 在客户机增多的情况下,...
    零度微笑_019c阅读 550评论 0 0
  • 练习材料 Lesson 3-1 Matterhorn man马特霍恩山区人 Modern alpinists tr...
    喵小园upup阅读 141评论 0 1
  • 关于整理,什么是重要的? 其实一直都知道什么是重要的,家庭,工作和成长! 家人孩子的陪伴,家人之间的感情于我是最最...
    微乴问阅读 199评论 0 0
  • 项目背景:项目是一个RN+原生混合项目,其中拍照是在RN进行的,拍照后图片要进行自定义裁剪和黑白素描处理、涂抹,是...
    vincent涵阅读 1,623评论 0 1
  • 前天黄昏,与两个朋友去吃自助餐。人多座少,生意一如平常火爆。我在餐厅内寻找座位时,来到一张四人桌旁边,座位上有两人...
    lalacat阅读 352评论 0 1