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

2019-06-05  本文已影响0人  金卡戴珊ugly

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

上一篇下一篇

猜你喜欢

热点阅读