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

44 篇文章 4 订阅

FileChannel管道流复制文件是基于nio的传输方式。速度上有30%的提升。其次在我的项目中使用传统FileOutputStream方式,在复制大文件时。进度打印出现迟滞。综合这两点选择使用FileChannel方案。本文章是简单使用多线程方式实现,个人测试文件读写在多线程下比单线程快4倍,有参考的小伙伴使用的话 还是最好使用线程池进行处理性能上的优化。

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");
                }
            } else if (msg.what == 3) {
                Log.e("jinchen", "源文件不存在");
            }
        };
    };

    /**
     * 复制单个文件
     */
    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) {
            if (progressDialog == null) {
                progressDialog = new CopyProgressDialog(context);
                progressDialog.setOnCancelListener(new CopyProgressDialog.OnCancelListener() {//点击返回取消时,关闭线程和流
                    @Override
                    public void onCancel() {
                        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());
                        }
                    }
                });
            }
            progressDialog.show();
            progressDialog.setNameText(oldPathName);
        }
        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();
                    }

//                    outbuff.flush();
//                    inbuff.close();
//                    outbuff.close();
//                    fileOutputStream.close();
//                    fileInputStream.close();
//                    fileChannelOutput.close();
//                    fileChannelInput.close();

                    if (progressDialog.isShowing()) {
                        progressDialog.dismiss();
                    }
                } catch (Exception e) {
                    Log.e("CopyPasteUtil", "CopyPasteUtil copyFile error:" + e.getMessage());
                } finally {
                    try {
                        outbuff.flush();
                        inbuff.close();
                        outbuff.close();
                        fileOutputStream.close();
                        fileInputStream.close();
                        fileChannelOutput.close();
                        fileChannelInput.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        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) {
                if (progressDialog == null) {
                    progressDialog = new CopyProgressDialog(context);
                    progressDialog.setOnCancelListener(new CopyProgressDialog.OnCancelListener() {//点击返回取消时,关闭线程和流
                        @Override
                        public void onCancel() {
                            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());
                            }
                        }
                    });
                }
                progressDialog.show();
                progressDialog.setNameText(sourceDir);
            }
        }
        run = new Runnable() {
            @Override
            public void run() {
//                (new File(targetDir)).mkdirs();
                File dst = new File(targetDir);
                if (!dst.exists()) {
                    dst.mkdir();
                }

                File src = new File(sourceDir);
                if (!src.exists()) {
                    Message message = handler.obtainMessage(3);
                    handler.sendMessage(message);
                }
                File[] file = src.listFiles(); // 获取源文件夹当下的文件或目录
                if (file != null) {
                    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();
//                        }
                        if (progress == 100) {
                            Log.e("jinchen", "copy over time " + System.currentTimeMillis());
                        }
                    }
                }
                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());
        } finally {
            try {
                outbuff.flush();
                inbuff.close();
                outbuff.close();
                fileOutputStream.close();
                fileInputStream.close();
                fileChannelOutput.close();
                fileChannelInput.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取文件夹大小
     * @param file
     */
    public static void getDirSize(File file) {
        if (!file.exists()) {
            Log.e("jinchen", "getDirSize 源文件不存在");
            return;
        }
        if (file.isFile()) {
            // 如果是文件,获取文件大小累加
            dirSize += file.length();
        } else if (file.isDirectory()) {
            File[] f1 = file.listFiles();
            if (f1 != null) {
                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);
    }

}

有很多小伙伴很懒的写dialog, 在这 我把dialog代码也贴出来 方便大家使用。xml我就不粘贴了,自己写下吧 很简单。

public class CopyProgressDialog extends Dialog {

    public OnCancelListener mOnCancelListener;
    private TextView tv_old_name;
    private TextView tv_progress;
    private TextView tv_mb;
    private Button cancel_btn;

    public CopyProgressDialog(@NonNull Context context) {
        super(context, R.style.TransparentDialog);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.dialog_progrees);
        setCanceledOnTouchOutside(false);

        tv_mb = findViewById(R.id.tv_mb);
        tv_progress = findViewById(R.id.tv_progress);
        tv_old_name = findViewById(R.id.tv_old_name);
        cancel_btn = findViewById(R.id.cancel_btn);

        cancel_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mOnCancelListener != null) {
                    mOnCancelListener.onCancel();
                }
            }
        });
    }

    public void setProgress(int progress) {

    }

    public void setProgressText(String progress) {
        tv_progress.setText(progress);
    }

    public void setFileVolumeText(String volume) {
        tv_mb.setText(volume);
    }

    public void setNameText(String name) {
        tv_old_name.setText(name);
    }

    public void setOnCancelListener(OnCancelListener onCancelListener) {
        this.mOnCancelListener = onCancelListener;
    }

    public interface OnCancelListener {
        void onCancel();
    }
}

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

 

 

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Java中可以使用FileChannel来进行文件压缩操作。具体步骤如下: 1. 创建一个输入流和一个输出流,用于读取原文件和输出压缩后的文件。 2. 创建一个输入通道和一个输出通道,将输入流和输出流分别封装成输入通道和输出通道。 3. 创建一个GZIPOutputStream对象,将输出通道作为参数传入,用于将数据压缩成gzip格式。 4. 创建一个ByteBuffer对象,用于存储读取的数据。 5. 循环读取原文件,将读取的数据存储到ByteBuffer中。 6. 将ByteBuffer中的数据写入GZIPOutputStream中,进行压缩。 7. 关闭输入流、输出流、输入通道、输出通道和GZIPOutputStream对象。 示例代码如下: ```java import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.zip.GZIPOutputStream; public class FileCompressor { public static void compressFile(String sourceFilePath, String destFilePath) throws Exception { FileInputStream fileInputStream = new FileInputStream(sourceFilePath); FileOutputStream fileOutputStream = new FileOutputStream(destFilePath); FileChannel inputChannel = fileInputStream.getChannel(); FileChannel outputChannel = fileOutputStream.getChannel(); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputChannel); ByteBuffer buffer = ByteBuffer.allocate(1024); while (inputChannel.read(buffer) != -1) { buffer.flip(); gzipOutputStream.write(buffer.array(), 0, buffer.limit()); buffer.clear(); } inputChannel.close(); outputChannel.close(); fileInputStream.close(); fileOutputStream.close(); gzipOutputStream.close(); } } ``` 注意:以上代码仅供参考,实际使用时需要进行适当的调整和完善。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值