java文件夹拷贝

这里主要分享文件夹拷贝(包括目录下的所有子目录以及文件)的两种方式。

第一种是字节流InputStream和OutputStream完成文件的复制,该方式不会使用使用缓冲区,不适合操作过大的文件夹;

第二种是使用nio中的FileChannel完成文件的复制,该方式会使用MappedByteBuffer作为缓冲区;

核心方法关注:

(1)private boolean copyFileImpl(File srcFile, File destFile);

(2)private boolean copyByFileChannel(File srcFile, File destFile);

package com.fc.work;

import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 实现文件拷贝,大文件不适用字节流全部读取
 *
 * @author author on 2019/9/10.
 */
public class FileCopyUtils {

    /**
     * 源文件,待拷贝
     */
    private File srcFile;

    /**
     * 目标文件
     */
    private File destFile;

    public FileCopyUtils(File srcFile, File destFile) {
        this.srcFile = srcFile;
        this.destFile = destFile;
    }

    public FileCopyUtils(String srcPath, String destPath) {
        this(new File(srcPath), new File(destPath));
    }

    /**
     * 传统的拷贝方式
     *
     * @return 拷贝结果
     */
    public boolean copy() {
        if (!this.srcFile.exists()) {
            System.out.println("要拷贝的文件找不到!");
            return false;
        }
        if (!this.destFile.getParentFile().exists()) {
            System.out.print("创建目标文件父路径:");
            System.out.println(this.destFile.getParentFile().mkdirs());
        }
        return copyByFileChannel(this.srcFile, this.destFile);
        //return copyFileImpl(this.srcFile, this.destFile);
    }

    /**
     * 拷贝文件的具体实现
     *
     * @param srcFile  源文件
     * @param destFile 目标文件
     * @return true/false
     */
    private boolean copyFileImpl(File srcFile, File destFile) {
        // 创建父路径
        if (!destFile.getParentFile().exists()) {
            boolean mkDestParentDirFlag = destFile.getParentFile().mkdirs();
            System.out.println("创建目标父路径:" + mkDestParentDirFlag);
        }
        // 定义固定长度的缓存字节数组作为缓冲区
        byte[] data = new byte[1024];
        // close()方法自动关闭流
        try (InputStream input = new FileInputStream(srcFile);OutputStream out = new FileOutputStream(destFile)){
            int len;
            while ((len = input.read(data)) != -1) {
                out.write(data, 0, len);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public boolean copyDir() {
        try {
            copyImpl(this.srcFile);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 拷贝目录或文件
     *
     * @param file 目录或文件
     */
    private void copyImpl(File file) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            // 即使是空目录,返回值也不会是null
            if (null != files && files.length > 0) {
                int length = files.length;
                for (int i = 0; i < length; i++) {
                    copyImpl(files[i]);
                }
            } else {
                // 空目录,直接新建目录
                String newFilePath = file.getPath().replace(this.srcFile.getPath().concat(File.separator), "");
                File newFile = new File(this.destFile.getPath(), newFilePath);
                if (!newFile.exists()) {
                    System.out.println("复制新目录:" + newFile.mkdir());
                }
            }
        } else {
            // 需要注意文件拷贝时目录问题
            String newFilePath = file.getPath().replace(this.srcFile.getPath().concat(File.separator), "");
            File newFile = new File(this.destFile.getPath(), newFilePath);
            //copyFileImpl(file, newFile);
            copyByFileChannel(file, newFile);
        }
    }

    /**
     * jdk1.9中提供了transferTo方法完成拷贝
     *
     * @return
     * @throws IOException
     */
    public void copyByTransfer() throws IOException {
        InputStream input = new FileInputStream(srcFile);
        OutputStream out = new FileOutputStream(destFile);
        //input.transferTo(out);
    }

    /**
     * FileChannel实现文件复制
     *
     * @param srcFile  源路径
     * @param destFile 目标目录
     * @return true/false
     */
    private boolean copyByFileChannel(File srcFile, File destFile) {
        if (!destFile.getParentFile().exists()) {
            boolean mkDestParentDirFlag = destFile.getParentFile().mkdirs();
            System.out.println("创建目标父路径:" + mkDestParentDirFlag);
        }
        try (FileChannel in = new FileInputStream(srcFile).getChannel(); FileChannel out = new FileOutputStream(destFile).getChannel()) {
            long size = in.size();
            MappedByteBuffer buf = in.map(FileChannel.MapMode.READ_ONLY, 0, size);
            out.write(buf);
            in.close();
            out.close();
            buf.clear();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

}

两种方式在文件夹不包含较大文件时效率接近,包含大文件时会有明显的效率差别,这里未做过多测试,只列举测试结果对比:

# 字节流方式
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
复制新目录:true
拷贝目录成功
拷贝耗时:676ms

# FileChannel方式
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
创建目标父路径:true
复制新目录:true
拷贝目录成功
拷贝耗时:552ms

 

你可能感兴趣的:(java文件夹拷贝)