• Java多种文件复制方式以及效率比较


    1 背景

    Java复制文件的方式其实有很多种,可以分为:

    • 传统的字节流读写复制FileInputStreamFileOutputStreamBufferedInputStreamBufferedOutputStream
    • 传统的字符流读写复制FileReaderFileWriterBufferWriterBufferedWriterBufferedReader
    • NIO系列的FileChannel
    • FileChannel+缓冲
    • java.nio.Files.copy()
    • 第三方包中的FileUtils.copy方法,比如org.apache.commons.io.FileUtilsorg.codehaus.plexus.util.FileUtils等等

    所以呢,看看各种方法效率怎么样,主要衡量的标准就是时间,另外的一些标准包括大文件的复制时的内存溢出等问题。

    2 概述

    由于很多时候复制文件都包括了文件夹下的所有子目录及文件的复制,所以笔者采用的遍历+复制方法去复制文件,就是把整个复制过程分为先遍历,遍历的过程中遇到文件夹就创建,遇到文件就调用不同的复制方法。

    遍历的5种方法:

    • File.listFiles()
    • File.list()
    • org.codehaus.plexus.util.FileUtils.getFiles()
    • org.apache.commons.io.FileUtils.listFiles()
    • java nio中的java.nio.file.Files.walkFileTree

    复制的8种方法:

    • FileInputStream+FileOutputStream
    • BufferedInputStream+BufferedOutputStream
    • FileReader+FileWriter
    • BufferedReader+BufferedWriter
    • FileChannel
    • FileChannel+buffer
    • org.apache.commons.io.FileUtils.copyFile()
    • java.nio.file.Files.copy()

    另外笔者不太想看控制台,所以配合了一点swing使用。

    3 遍历

    3.1 listFiles()

     private static void traverseByListFiles(File srcFile,File desFile) throws IOException
    {
    	if(srcFile.isDirectory())
    	{
    		File[] files = srcFile.listFiles();
    		assert files != null;
    		for(File file : files)
    		{
    			File desFileOrDir = new File(desFile.getAbsolutePath() + File.separator + file.getName());
    			if(file.isDirectory())
    			{
    				if(desFileOrDir.exists())
    					desFileOrDir.delete();
    				desFileOrDir.mkdirs();
    			}
    			traverseByListFiles(file, desFileOrDir);
    		}
    	}
    	else 
    	{
    		copyFile(srcFile, desFile);
    	}
    }
    

    通过srcFilelistFiles()获取所有的子文件与子文件夹,然后判断是否是目录:

    • 如果是目录,首先判断有没有这个文件(有时候本来是文件夹但是却存在同名的文件,就先删除),再创建文件夹,然后递归执行函数
    • 如果不是目录,直接把两个File作为参数进行文件复制,里面用什么方法后面会设置

    3.2 list()

    private static void traverseByList(File srcFile,File desFile) throws IOException
    {
    	if (srcFile.isDirectory())
    	{
    		String[] files = srcFile.list();
    		assert files != null;
    		for (String file : files)
    		{
    			File subSrcFile = new File(srcFile, file);
    			File subDesFile = new File(desFile, file);
    			if (subSrcFile.isDirectory())
    			{
    				if (subDesFile.exists())
    					subDesFile.delete();
    				subDesFile.mkdirs();
    			}
    			traverseByList(subSrcFile, subDesFile);
    		}
    	}
    	else
    	{
    		copyFile(srcFile, desFile);
    	}
    }
    

    list与第一种listFiles()类似,不过是String[],也是先判断目录,创建目录,不是目录直接复制。

    3.3 org.codehaus.plexus.util.FileUtils.getFiles

    private static void traverseByGetFiles(File srcFile, File desFile) throws IOException
    {
    	if (srcFile.isDirectory())
    	{
    		java.util.List<File> fileList = org.codehaus.plexus.util.FileUtils.getFiles(srcFile,null,null);
    		for (File file : fileList)
    		{
    			File desFileOrDir = new File(desFile.getAbsolutePath() + File.separator + file.getName());
    			if(file.isDirectory())
    			{
    				if(desFileOrDir.exists())
    					desFileOrDir.delete();
    				desFileOrDir.mkdirs();
    			}
    			traverseByListFiles(file, desFileOrDir);
    		}
    	}
    	else
    	{
    		copyFile(srcFile, desFile);
    	}
    }
    

    这是用了别人的工具类进行遍历.

    org.codehaus.plexus.util.FileUtils.getFiles(srcFile,null,null);
    

    返回的结果是java.util.List

    3.4 Commons.io

    private static void traverseByCommonsIO(File srcFile, File desFile) throws IOException
    {
    	if (srcFile.isDirectory())
    	{
    		Collection<File> files = org.apache.commons.io.FileUtils.listFiles(srcFile,null,false);
    		for (File file : files)
    		{
    			File desFileOrDir = new File(desFile.getAbsolutePath() + File.separator + file.getName());
    			if(file.isDirectory())
    			{
    				if(desFileOrDir.exists())
    					desFileOrDir.delete();
    				desFileOrDir.mkdirs();
    			}
    			traverseByCommonsIO(file, desFileOrDir);
            }
    	}
    	else 
    	{
    		copyFile(srcFile, desFile);
    	}
    }
    

    使用org.apache.commons.io.FileUtilslistFiles方法,参数为要遍历的目录,一个null和一个false,第二个参数表示过滤器,表示过滤出特定后缀名的文件,类型为String [],第三个布尔参数表示是否递归访问子目录。

    3.5 walkFileTree

    利用FileVisitor这个接口,实际中常用SimpleFileVisitor

    private static void traverseByNIO2(File srcFile) throws IOException
    {
    	java.nio.file.Files.walkFileTree(srcFile.toPath(), new SimpleFileVisitor<>() {
    		@Override
    		public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException 
    		{
    			File d = new File(des.toString() + path.toAbsolutePath().toString().substring(src.toString().length()));
                new File(d.toString().substring(0, d.toString().lastIndexOf(File.separator))).mkdirs();
                copyFile(path.toFile(), d);
                return FileVisitResult.CONTINUE;
            }
        });
    }
    

    FileVisitor接口定义了四个方法,分别为:

    public interface FileVisitor<T>
    {
    	FileVisitResult preVisitDirectory(T dir,BasicFileAttributes attrs)
    	{
    		//访问dir前的操作,dir类型一般为java.nio.Path
    	}
    	
    	FileVisitResult postVisitDirectory(T dir,BasicFileAttributes attrs)
    	{
    		//访问dir后的操作
    	}
    	
    	FileVisitResult visitFile(T file,BasicFileAttributes attrs)
    	{
    		//访问file时的操作
    	}
    	
    	FileVisitResult visitFileFailed(T file,BasicFileAttributes attrs)
    	{
    		//访问file失败时的操作
    	}
    }
    

    在上面的例子中只是实现了visitFile,因为只是复制操作,首先判断是否是源目录的路径,不是的话创建文件夹再复制文件。

    这里说一下返回值FileVisitResultFileVisitResult是一个枚举类型,根据返回值判断是否继续遍历,可取值如下:

    • CONTINUE:继续
    • TERMINNATE:结束
    • SKIP_SIBLINGS:继续,跳过同一目录的节点
    • SKIP_SUBTREE:继续,跳过子目录,但会访问子文件

    4 复制

    4.1 FileInputStream+FileOutputStream

    首先是经典的字节流FileInputStream+FileOutputStream,这个比较简单,使用FileInputStream读取后使用FileOutputStream写入,不过效率嘛.....一般般。

    private static void copyByFileStream(File srcFile,File desFile) throws IOException
    {
    	FileInputStream inputStream = new FileInputStream(srcFile);
    	FileOutputStream outputStream = new FileOutputStream(desFile);
    	byte [] b = new byte[1024];
    	while(inputStream.read(b) != -1)
    	{
    		outputStream.write(b);
    		addCopySize();
    	}
    	inputStream.close();
    	outputStream.close();
    }
    

    注意一下三个read()的区别:

    • input.read()
    • input.read(b)
    • input.read(b,off,len)

    4.1.1 read()

    逐个字节进行读取,返回int,写入时直接使用write(n)

    int n = input.read();
    output.write(n);
    

    这个可以说是三个read中最慢的....测试了一个2G左右的文件,用了大概10分钟才复制160M。

    4.1.2 read(b)

    参数是一个byte [],将字节缓冲到其中,返回数组的字节个数,这个比read()快很多:

    byte [] b = new byte[1024];
    while(input.read(b) != -1){
    	output.write(b);
    }
    

    4.1.3 read(b,off,len)

    这个方法其实和read(b)差不多,read(b)相当于省略了参数的read(b,off,len)

    byte [] b = new byte[1024];
    int n;
    while((n = input.read(b,0,1024))!=-1){
    	output.write(b,0,n);
    }
    
    public int read(byte b[], int off, int len) throws IOException 
    {
    	return readBytes(b, off, len);
    }
    
    public int read(byte b[]) throws IOException 
    {
    	return readBytes(b, 0, b.length);
    }
    

    这两个都是调用一样的readBytes()

    private native int readBytes(byte b[], int off, int len) throws IOException;
    

    至于效率...可以看看结果,使用10G内的小文件进行测试:

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    可以看到,没有哪个一定比另外一个更快(不过最后一个误差有点太大了?7G不够的文件)。

    采用哪一个建议自己去测试,毕竟这存在很多误差,比如文件,Java版本,机器本身等等,仅供参考。

    4.2 BufferedInputStream+BufferedOutputStream

    缓冲字节流BufferedInputStream+BufferedOutputStream,相比起FileInputStreamBufferedInputStream读取时会先从缓冲区读取数据,缓冲区无可读数据再从文件读取,所以会比FileInputStream快。

    private static void copyByBufferStream(File srcFile,File desFile) throws IOException
    {
    	BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(srcFile));
    	BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(desFile));
    	byte [] b = new byte[1024];
    	while(inputStream.read(b) != -1)
    	{
    		addCopySize();
    		outputStream.write(b);
    	}
    	inputStream.close();
    	outputStream.close();
    }
    

    这里也说一下BufferedInputStream的三个read

    • read(b)
    • read(b,off,len)
    • readAllBytes()

    4.2.1 read(b)

    这个其实和FileInputStream的那个没啥区别,把一个字节数组仍进去就好了。

    4.2.2 read(b,off,len)

    这个....也和FileInputStream那个没啥区别,不说了。

    4.2.3 readAllBytes()

    这个一次可以读取所有的字节。不过用这个虽然省事,可以直接

    output.write(input.readAllBytes());
    

    但是呢,有代价的:

    在这里插入图片描述

    会出现OutOfMemory错误,就是对于大文件还是老老实实分开吧。

    看看效率:

    在这里插入图片描述

    readAllBytes对于大文件(测试的是5G内的文件)直接爆内存....

    在这里插入图片描述

    在这里插入图片描述

    readAllBytes()又爆了.....这个才2G不到的文件...readAllBytes()看来不是很给力啊....不过对于小文件效率还可以接受。

    4.3 FileReader+FileWriter

    字符流读写FileReader+FileWriter,相比起字节流的read,基本上把byte[]换成char[]即可,因为是逐个字符读取,而字节流是逐个字节读取因此采用byte[]

    注意这个不能用来读取图片、音乐等文件,不然复制出来的文件打不开。

    private static void copyByFileReader(File srcFile,File desFile) throws IOException
    {
    	FileReader reader = new FileReader(srcFile);
    	FileWriter writer = new FileWriter(desFile);
    
    	char [] c = new char[1024];
    	while(reader.read(c) != -1)
    	{
    		addCopySize();
    		writer.write(c);
    	}
    	reader.close();
    	writer.close();
    }
    

    4.4 BufferedReader+BufferedWriter

    缓冲字符流读写BufferedReader+BufferedWriterBufferedReader相比起FileReader有一个readLine()方法,可以每行读入,会比FileReader快。对应的BufferedWriter提供了write(String)方法,当然也有write(String s,int off,int len),同样这个不能用来读取图片等。

    private static void copyByBufferReader(File srcFile,File desFile) throws IOException
    {
    	BufferedReader reader = new BufferedReader(new FileReader(srcFile));
    	BufferedWriter writer = new BufferedWriter(new FileWriter(desFile));
    
    	char [] c = new char[1024];
    	while(reader.read(c) != -1)
    	{
    		addCopySize();
    		writer.write(c);
    	}
    	reader.close();
    	writer.close();
    }
    

    4.5 FileChannel

    通过FileChannel复制,首先通过FileInputStreamFileOutputStream打开流,再用getChannel()方法。最后使用transferTo()transferFrom()进行复制,一条语句即可,十分方便,而且效率很高。

    private static void copyByFileChannel(File srcFile,File desFile) throws IOException
    {
    	FileChannel srcChannel = new FileInputStream(srcFile).getChannel();
    	FileChannel desChannel = new FileOutputStream(desFile).getChannel();
    	srcChannel.transferTo(0,srcChannel.size(),desChannel);
    	srcChannel.close();
    	desChannel.close();
    }
    

    4.6 FileChannel+ByteBuffer

    在利用了FileInputStreamFileOutputStream打开了FileChannel的基础上,配合ByteBuffer使用。

    private static void copyByFileChannelWithBuffer(File srcFile,File desFile) throws IOException
    {
    	FileChannel srcChannel = new FileInputStream(srcFile).getChannel();
    	FileChannel desChannel = new FileOutputStream(desFile).getChannel();
    	ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
    	while(srcChannel.read(buffer) != -1)
    	{
    		buffer.flip();
    		desChannel.write(buffer);
    		buffer.clear();
    		addCopySize();
    	}
    	srcChannel.close();
    	desChannel.close();
    }
    

    flip的意思是"翻转":

    buffer.flip();
    

    Buffer从写模式变为读模式,接着write(buffer),再清空。

    看看这两种方法效率:

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    另外测试的时候发现transferTo的"上限"为2G,就是对于大于2G的单个文件最多最能复制2个G。

    所以...对于大文件没有可比性了。

    4.7 FileUtils.copyFile()

    这是工具类,没啥好说的,参数是两个File,分别表示源与目标。

    private static void copyByCommonsIO(File srcFile,File desFile) throws IOException
    {
    	FileUtils.copyFile(srcFile, desFile);
    }
    

    4.8 Files.copy()

    这是官方提供的Files工具类,前两个参数为Path,分别表示源与目标,可以设置第三个参数(或者省略),表示选项。例如可以设置StandardCopyOption.REPLACE_EXISTING

    private static void copyByFiles(File srcFile,File desFile) throws IOException
    {
    	Files.copy(srcFile.toPath(), desFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
    }
    

    注意Files.copy会保持文件的隐藏属性,原来是隐藏的文件复制后也是隐藏的,以上7种则不会。

    5 其他

    5.1 swing布局

    5.1.1 网格布局

    JFrame采用了网格布局

    setLayout(new GridLayout(3,1,5,3));
    

    三行一列,因为只要三个按钮,选择源文件(夹)、选择目标文件夹、选择遍历方式。

    选择遍历方式/复制方式的JFrame同样使用了网格布局:

    showTraverseMethod.setLayout(new GridLayout(5,1,3,3));
    showCopyMethod.setLayout(new GridLayout(4,2,5,5));
    

    5.1.2 居中

    setBounds(
    (int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - 200,
    (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - 200, 
    400, 400);
    

    高400,宽400,利用ToolKit.getDefaultToolKit().getScreenSize()获取屏幕的高度和宽度实现居中。

    5.1.3 组件的添加与删除

    由于在主JFrame中只有三个按钮,选择完遍历方式后需要更新这个组件,这里的实现是先删除这个组件在添加组件:

    traverseMethodButton.setVisible(false);
    remove(traverseMethodButton);
    add(copyMethodButton);
    copyMethodButton.setVisible(true);
    

    设置它不可见再删除,再添加另一组件,再设置可见。

    5.2 进度条

    进度条这个东西把搞了很久......其实就是新建一个线程就可以了。

    核心代码:

    new Thread(
    	() ->
    	{
    		int percent;
    		while ((percent = getCopyPercent()) < 100)
    		{
    			try
    			{
    				Thread.sleep(100);
    			}
    			catch(InterruptedException e)
    			{
    				e.printStackTrace();
    			}
    			copyProgressBar.setValue(percent);
    		}
    	}
    ).start();
    

    JProgressBar是直接添加在一个JFrame中的,不用什么太复杂的布局。

    获取百分比后调用setValue(),一定要新建一个线程操作,不然不能正常显示进度条。

    另外复制的操作建议使用SwingWorker

    SwingWorker<String,Object> copyTask = new SwingWorker<>()
    {
    	@Override
    	protected String doInBackground()
    	{
    		try
    		{
    			if (traverseMethod[0])
    				traverseByListFiles(src, des);
    			else if (traverseMethod[1])
    				traverseByList(src, des);
    			else if (traverseMethod[2])
    				traverseByGetFiles(src, des);
    			else if (traverseMethod[3])
    				traverseByCommonsIO(src, des);
    			else if (traverseMethod[4])
    				traverseByNIO2(src);
    			else
    			{
    				showProgressBar.dispose();
    				showMessage("遍历失败,找不到遍历方法");
    			}
    		}
    		catch (IOException e)
    		{
    			e.printStackTrace();
    			showProgressBar.dispose();
    			showMessage("未知错误复制失败");
    		}
    		finish(start);
    		return null;
    	}
    };
    copyTask.execute();
    

    6 测试

    下面进行测试。测试的对象包括:

    • 1G文件
    • 10G文件
    • 1G目录
    • 10G目录

    6.1 1G文件

    在这里插入图片描述

    文件的话其实纵向比较即可,因为基本不用怎么遍历,横向比较可以勉强看作求平均值。

    对于非文本文件,FileReader/WriterBufferedReader/Writer没有太大的参考意义,比如复制视频文件是打不开的,而且复制出来的文件会变大。对于单文件Files.copy的性能非常好,NIO果然厉害。

    6.2 10G文件

    在这里插入图片描述

    这个10G的文件是文本文件。

    现在可以看看FileChannel的这一行,明显所花的时间要比其他要少,为什么呢?

    因为文件大于2G,FileChanneltrasferTo方法只能写入最多2G的文件,所以对于大于2G的文件复制出来只有2G,因此FileChannel的这一行没有太大可比性。对于文本文件,BufferedReader/Writer的复制速度是最快的了,其次是FileInput/OutputStream。对于单个大文件,FileUtilsFiles.copy的速度比FileInputStream慢。

    6.3 1G目录

    在这里插入图片描述

    对于目录的话可以考虑放弃BufferedReaderFileReader了,除非全部是文本文件,否则推荐使用BufferedInput/OutputStreamFiles.copy()进行复制,工具类FileUtils的复制方法表现还是不错的,但相比起标准的Files.copy效率差了。

    对于FileChannel与配合缓冲使用的FileChannel,1G的话好像不相上下。

    遍历方式的话...可以看到plexus的遍历方法表现差距很大,而ApachelistFiles或者Java NIOwalkFileTree比较稳定且速度还可以,推荐使用这两种方式遍历目录。

    6.4 10G目录

    在这里插入图片描述

    FileReaderBufferedReader这两行可以忽略了。对于小文件用FileChannel的话还是不错的,对于大文件一定要用FileChannel的话可以配合ByteBuffer使用,不过从数据上看效果比BufferedInput/OutputStream要低。

    再看看org.apache.commons.io.FileUtilsjava.nio.file.Files的复制,差别不太,效果接近,但在1G的时候差距有点大。

    遍历方式的话,walkFileTrees最快。

    当然这些测试仅供参考,具体使用哪一个要看看具体环境,另外这种方式把遍历与复制分开,ApacheFileUtils有方法可以直接复制目录的,因此,使用哪个更合适还需要个人具体测试。

    7 源码

    笔者比较偷懒代码全部仍在一个文件了:

    import java.awt.*;
    import javax.swing.*;
    import java.nio.*;
    import java.nio.channels.*;
    import java.io.*;
    import java.nio.file.*;
    import java.nio.file.attribute.*;
    import java.util.*;
    import org.apache.commons.io.*;
    
    public class Test extends JFrame
    {
        public static final long serialVersionUID = 12398129389122L;
    
        private JFrame showTraverseMethod = new JFrame("遍历方式");
        private JFrame showCopyMethod = new JFrame("复制方式");
    
        private JButton traverseMethodButton = new JButton("请选择遍历方式");
        private JButton copyMethodButton = new JButton("请选择复制方式");
        private JButton copyButton = new JButton("开始复制");
    
        private JButton traverseByListFiles = new JButton("File.listFiles()");
        private JButton traverseByList = new JButton("File.list()");
        private JButton traverseByGetFiles = new JButton("(plexus)getFiles()");
        private JButton traverseByCommonsIO = new JButton("Commons IO");
        private JButton traverseByNIO2 = new JButton("NIO2");
    
        private JButton copyByFileStream = new JButton("File stream");
        private JButton copyByBufferStream = new JButton("Buffer stream");
        private JButton copyByFileReader = new JButton("File reader");
        private JButton copyByBufferReader = new JButton("Buffer reader");
        private JButton copyByFileChannel = new JButton("File channel");
        private JButton copyByFileChannelWithBuffer = new JButton("File channel with buffer");
        private JButton copyByCommonsIO = new JButton("Commons IO");
        private JButton copyByFiles = new JButton("Files.copy");
    
        public Test()
        {
            JButton src = new JButton("选择源文件(夹)");
            src.addActionListener(
                event ->
                {
                    JFileChooser fileChooser = new JFileChooser();
                    fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
                    fileChooser.showDialog(new Label(), "选择文件(夹)");
                    FilesCopy.setSrc(fileChooser.getSelectedFile());
                }
            );
            JButton des = new JButton("选择目标文件夹");
            des.addActionListener(
                event ->
                {
                    JFileChooser fileChooser = new JFileChooser();
                    fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
                    fileChooser.showDialog(new JLabel(),"选择文件夹");
                    FilesCopy.setDes(fileChooser.getSelectedFile());
                }
            );
    
            traverseMethodButton.addActionListener(
                event ->
                {
                    traverseByListFiles.addActionListener(
                        e->
                        {
                            FilesCopy.setTraverseByListFiles();
                            showTraverseMethod.dispose();
                        }
                    );
    
                    traverseByList.addActionListener(
                        e ->
                        {
                            FilesCopy.setTraverseByList();
                            showTraverseMethod.dispose();
                        }
                    );
    
                    traverseByGetFiles.addActionListener(
                        e ->
                        {
                            FilesCopy.setTraverseByGetfiles();
                            showTraverseMethod.dispose();
                        }
                    );
    
                    traverseByCommonsIO.addActionListener(
                        e ->
                        {
                            FilesCopy.setTraverseByCommonsIO();
                            showTraverseMethod.dispose();
                        }
                    );
    
                    traverseByNIO2.addActionListener(
                        e ->
                        {
                            FilesCopy.setTraverseByNIO2();
                            showTraverseMethod.dispose();
                        }
                    );
    
    
                    showTraverseMethod.setLayout(new GridLayout(5,1,3,3));
                    showTraverseMethod.setTitle("遍历方式");
                    showTraverseMethod.setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - 200,
                            (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - 200, 400, 400);
                    showTraverseMethod.setVisible(true);
                    showTraverseMethod.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
                    showTraverseMethod.add(traverseByListFiles);
                    showTraverseMethod.add(traverseByList);
                    showTraverseMethod.add(traverseByGetFiles);
                    showTraverseMethod.add(traverseByCommonsIO);
                    showTraverseMethod.add(traverseByNIO2);
    
                    traverseMethodButton.setVisible(false);
                    remove(traverseMethodButton);
                    add(copyMethodButton);
                    copyMethodButton.setVisible(true);
                }
            );
    
            copyMethodButton.addActionListener(
                event ->
                {
                    copyByFileStream.addActionListener(
                        e ->
                        {
                            FilesCopy.setCopyByFileStream();
                            showCopyMethod.dispose();
                        }
                    );
    
                    copyByBufferStream.addActionListener(
                        e ->
                        {
                            FilesCopy.setCopyByBufferStream();
                            showCopyMethod.dispose();
                        }
                    );
    
                    copyByFileReader.addActionListener(
                        e ->
                        {
                            FilesCopy.setCopyByFileReader();
                            showCopyMethod.dispose();
                        }
                    );
    
                    copyByBufferReader.addActionListener(
                        e ->
                        {
                            FilesCopy.setCopyByBufferReader();
                            showCopyMethod.dispose();
                        }
                    );
    
                    copyByFileChannel.addActionListener(
                        e ->
                        {
                            FilesCopy.setCopyByFileChannel();
                            showCopyMethod.dispose();
                        }
                    );
    
                    copyByFileChannelWithBuffer.addActionListener(
                        e ->
                        {
                            FilesCopy.setCopyByFileChannelWithBuffer();
                            showCopyMethod.dispose();
                        }
                    );
    
                    copyByCommonsIO.addActionListener(
                        e ->
                        {
                            FilesCopy.setCopyByCommonsIO();
                            showCopyMethod.dispose();
                        }
                    );
    
                    copyByFiles.addActionListener(
                        e ->
                        {
                            FilesCopy.setCopyByFiles();
                            showCopyMethod.dispose();
                        }
                    );
    
                    showCopyMethod.setLayout(new GridLayout(4,2,5,5));
                    showCopyMethod.setTitle("复制方式");
                    showCopyMethod.setBounds(
                            (int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - 200,
                            (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - 200, 400, 400);
                    showCopyMethod.setVisible(true);
                    showCopyMethod.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    showCopyMethod.add(copyByFileStream);
                    showCopyMethod.add(copyByBufferStream);
                    showCopyMethod.add(copyByFileReader);
                    showCopyMethod.add(copyByBufferReader);
                    showCopyMethod.add(copyByFileChannel);
                    showCopyMethod.add(copyByFileChannelWithBuffer);
                    showCopyMethod.add(copyByCommonsIO);
                    showCopyMethod.add(copyByFiles);
    
                    copyMethodButton.setVisible(false);
                    remove(copyMethodButton);
                    add(copyButton);
                    copyButton.setVisible(true);
                }
            );
    
            copyButton.addActionListener(
                event ->
                {
                    if(FilesCopy.haveSelectedSrcAndDes())
                    {
                        FilesCopy.copy();
                        copyButton.setVisible(false);
                        remove(copyButton);
                        add(traverseMethodButton);
                        traverseMethodButton.setVisible(true);
                    }
                    else
                        JOptionPane.showMessageDialog(null,"请先选择源文件(夹)与目标文件夹!");
                }
            );
    
            setLayout(new GridLayout(3,1,5,3));
            setTitle("复制文件");
            setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - 200,
                    (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - 200, 400, 400);
            setVisible(true);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            add(src);
            add(des);
            add(traverseMethodButton);
        }
    
        public static void main(String[] args) {
            new Test();
        }
    }
    
    class FilesCopy
    {
        private static File src = null;
        private static File des = null;
        private static long desSize = 0;
        private static long srcSize = 0;
        private static boolean [] traverseMethod = {false,false,false,false,false,false};
        private static boolean[] copyMethod = { false, false, false, false, false, false ,false,false};
        private static JFrame showProgressBar = new JFrame();
        private static JProgressBar copyProgressBar = new JProgressBar();
        private static JTextField textField = new JTextField();
        private static int index = 0;
    
        private static int getCopyPercent()
        {
            return (int)(desSize * 100.0 / srcSize);
        }
    
        private static void addCopySize() {
            desSize += 1024L;
        }
    
        public static void setTraverseByListFiles()
        {
            traverseMethod[0] = true;
        }
    
        private static void traverseByListFiles(File srcFile,File desFile) throws IOException
        {
            if(srcFile.isDirectory())
            {
                File[] files = srcFile.listFiles();
                assert files != null;
                for(File file : files)
                {
                    File desFileOrDir = new File(desFile.getAbsolutePath() + File.separator + file.getName());
                    if(file.isDirectory())
                    {
                        if(desFileOrDir.exists())
                            desFileOrDir.delete();
                        desFileOrDir.mkdirs();
                    }
                    traverseByListFiles(file, desFileOrDir);
                }
            }
            else {
                copyFile(srcFile, desFile);
            }
        }
    
        public static void setTraverseByList()
        {
            traverseMethod[1] = true;
        }
    
        private static void traverseByList(File srcFile,File desFile) throws IOException
        {
            if (srcFile.isDirectory())
            {
                String[] files = srcFile.list();
                assert files != null;
                for (String file : files)
                {
                    File subSrcFile = new File(srcFile, file);
                    File subDesFile = new File(desFile, file);
                    if (subSrcFile.isDirectory())
                    {
                        if (subDesFile.exists())
                            subDesFile.delete();
                        subDesFile.mkdirs();
                    }
                    traverseByList(subSrcFile, subDesFile);
                }
            }
            else
            {
                copyFile(srcFile, desFile);
            }
    
        }
    
        public static void setTraverseByGetfiles()
        {
            traverseMethod[2] = true;
        }
    
        private static void traverseByGetFiles(File srcFile, File desFile) throws IOException
        {
            if (srcFile.isDirectory())
            {
                java.util.List<File> fileList = org.codehaus.plexus.util.FileUtils.getFiles(srcFile,null,null);
                for (File file : fileList)
                {
                    File desFileOrDir = new File(desFile.getAbsolutePath() + File.separator + file.getName());
                    if(file.isDirectory())
                    {
                        if(desFileOrDir.exists())
                            desFileOrDir.delete();
                        desFileOrDir.mkdirs();
                    }
                    traverseByListFiles(file, desFileOrDir);
                }
            }
            else
            {
                copyFile(srcFile, desFile);
            }
        }
    
        public static void setTraverseByCommonsIO()
        {
            traverseMethod[3] = true;
        }
    
        private static void traverseByCommonsIO(File srcFile, File desFile) throws IOException
        {
            if (srcFile.isDirectory())
            {
                Collection<File> files = org.apache.commons.io.FileUtils.listFiles(srcFile,null,false);
                for (File file : files)
                {
                    File desFileOrDir = new File(desFile.getAbsolutePath() + File.separator + file.getName());
                    if(file.isDirectory())
                    {
                        if(desFileOrDir.exists())
                            desFileOrDir.delete();
                        desFileOrDir.mkdirs();
                    }
                    traverseByCommonsIO(file, desFileOrDir);
                }
            }
            else {
                copyFile(srcFile, desFile);
            }
        }
    
        public static void setTraverseByNIO2()
        {
            traverseMethod[4] = true;
        }
    
        private static void traverseByNIO2(File srcFile) throws IOException
        {
            java.nio.file.Files.walkFileTree(srcFile.toPath(), new SimpleFileVisitor<>() {
                @Override
                public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
                    File d = new File(des.toString() + path.toAbsolutePath().toString().substring(src.toString().length()));
                    new File(d.toString().substring(0, d.toString().lastIndexOf(File.separator))).mkdirs();
                    copyFile(path.toFile(), d);
                    return FileVisitResult.CONTINUE;
                }
            });
        }
    
        public static void setCopyByFileStream()
        {
            copyMethod[0] = true;
        }
    
        private static void copyByFileStream(File srcFile,File desFile) throws IOException
        {
            FileInputStream inputStream = new FileInputStream(srcFile);
            FileOutputStream outputStream = new FileOutputStream(desFile);
            byte [] b = new byte[1024];
            while(inputStream.read(b) != -1)
            {
                outputStream.write(b);
                addCopySize();
            }
            inputStream.close();
            outputStream.close();
        }
    
        public static void setCopyByBufferStream()
        {
            copyMethod[1] = true;
        }
    
        private static void copyByBufferStream(File srcFile,File desFile) throws IOException
        {
            BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(srcFile));
            BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(desFile));
            byte [] b = new byte[1024];
            while(inputStream.read(b) != -1)
            {
                addCopySize();
                outputStream.write(b);
            }
            inputStream.close();
            outputStream.close();
        }
    
        public static void setCopyByFileReader()
        {
            copyMethod[2] = true;
        }
    
        private static void copyByFileReader(File srcFile,File desFile) throws IOException
        {
            FileReader reader = new FileReader(srcFile);
            FileWriter writer = new FileWriter(desFile);
    
            char [] c = new char[1024];
            while(reader.read(c) != -1)
            {
                addCopySize();
                writer.write(c);
            }
            reader.close();
            writer.close();
        }
    
        public static void setCopyByBufferReader()
        {
            copyMethod[3] = true;
        }
    
        private static void copyByBufferReader(File srcFile,File desFile) throws IOException
        {
            BufferedReader reader = new BufferedReader(new FileReader(srcFile));
            BufferedWriter writer = new BufferedWriter(new FileWriter(desFile));
    
            char [] c = new char[1024];
            while(reader.read(c) != -1)
            {
                addCopySize();
                writer.write(c);
            }
            reader.close();
            writer.close();
        }
    
        public static void setCopyByFileChannel()
        {
            copyMethod[4] = true;
        }
    
        private static void copyByFileChannel(File srcFile,File desFile) throws IOException
        {
            FileChannel srcChannel = new FileInputStream(srcFile).getChannel();
            FileChannel desChannel = new FileOutputStream(desFile).getChannel();
            srcChannel.transferTo(0,srcChannel.size(),desChannel);
            srcChannel.close();
            desChannel.close();
        }
    
        public static void setCopyByFileChannelWithBuffer()
        {
            copyMethod[5] = true;
        }
    
        private static void copyByFileChannelWithBuffer(File srcFile,File desFile) throws IOException
        {
            FileChannel srcChannel = new FileInputStream(srcFile).getChannel();
            FileChannel desChannel = new FileOutputStream(desFile).getChannel();
            ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
            while(srcChannel.read(buffer) != -1)
            {
                buffer.flip();
                desChannel.write(buffer);
                buffer.clear();
                addCopySize();
            }
            srcChannel.close();
            desChannel.close();
        }
    
        public static void setCopyByCommonsIO()
        {
            copyMethod[6] = true;
        }
    
        private static void copyByCommonsIO(File srcFile,File desFile) throws IOException
        {
            FileUtils.copyFile(srcFile, desFile);
        }
    
        public static void setCopyByFiles()
        {
            copyMethod[7] = true;
        }
    
        private static void copyByFiles(File srcFile,File desFile) throws IOException
        {
            Files.copy(srcFile.toPath(), desFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
        }
    
        public static void setSrc(File srcFile) {
    		src = srcFile;
    		if(srcFile.isDirectory())
    		    srcSize = org.apache.commons.io.FileUtils.sizeOfDirectory(srcFile);
    		else
                srcSize = src.length();
    	}
    
        public static void setDes(File desFile) {
            des = desFile;
            desSize = 0;
        }
    
        public static void setSrc(Path srcPath)
        {
            setSrc(srcPath.toFile());
        }
    
        public static void setDes(Path desPath)
        {
            setDes(desPath.toFile());
        }
    
        private static void copyFile(File srcFile,File desFile) throws IOException
        {
            if (copyMethod[0])
                copyByFileStream(srcFile,desFile);
            else if (copyMethod[1])
                copyByBufferStream(srcFile, desFile);
            else if (copyMethod[2])
                copyByFileReader(srcFile, desFile);
            else if (copyMethod[3])
                copyByBufferReader(srcFile, desFile);
            else if (copyMethod[4])
                copyByFileChannel(srcFile, desFile);
            else if (copyMethod[5])
                copyByFileChannelWithBuffer(srcFile, desFile);
            else if (copyMethod[6])
                copyByCommonsIO(srcFile, desFile);
            else if (copyMethod[7])
                copyByFiles(srcFile, desFile);
            else
                showMessage("复制失败,找不到复制方法.");
        }
    
        private static void showMessage(String message)
        {
            JOptionPane.showMessageDialog(null, message);
        }
    
        public static boolean haveSelectedSrcAndDes()
        {
            return src != null && des != null;
        }
    
        public static void copy()
        {
            long start = System.currentTimeMillis();
            if(haveSelectedSrcAndDes())
            {
                if(src.isFile())
                {
                    des = new File(des.getAbsolutePath()+File.separator+src.getName());
                }
                SwingWorker<String,Object> copyTask = new SwingWorker<>()
                {
                    @Override
                    protected String doInBackground()
                    {
                        try
                        {
                            if (traverseMethod[0])
                                traverseByListFiles(src, des);
                            else if (traverseMethod[1])
                                traverseByList(src, des);
                            else if (traverseMethod[2])
                                traverseByGetFiles(src, des);
                            else if (traverseMethod[3])
                                traverseByCommonsIO(src, des);
                            else if (traverseMethod[4])
                                traverseByNIO2(src);
                            else
                            {
                                showProgressBar.dispose();
                                showMessage("遍历失败,找不到遍历方法");
                            }
                        }
                        catch (IOException e)
                        {
                            e.printStackTrace();
                            showProgressBar.dispose();
                            showMessage("未知错误复制失败");
                        }
                        finish(start);
                        return null;
                    }
                };
                copyTask.execute();
                if (!copyMethod[4] && !copyMethod[6] && !copyMethod[7])
                {
                    copyProgressBar.setMinimum(0);
                    copyProgressBar.setMaximum(100);
                    copyProgressBar.setValue(0);
                    copyProgressBar.setVisible(true);
                    copyProgressBar.setStringPainted(true);
    
                    showProgressBar.add(copyProgressBar);
                    showProgressBar.setTitle("复制进度");
                    showProgressBar.setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - 150,
                            (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - 50, 300, 100);
                    showProgressBar.setVisible(true);
                    new Thread(
                            () ->
                            {
                                int percent;
                                while ((percent = getCopyPercent()) < 100)
                                {
                                    try
                                    {
                                        Thread.sleep(100);
                                    }
                                    catch(InterruptedException e)
                                    {
                                        e.printStackTrace();
                                    }
                                    copyProgressBar.setValue(percent);
                                }
                            }
                    ).start();
                }
                else
                {
    
                    final String [] text = {".","..","...","....",".....",".......","......",".....","....","...","..","."};
                    textField.setVisible(true);
                    textField.setHorizontalAlignment(JTextField.CENTER);
                    textField.setEditable(false);
                    showProgressBar.add(textField);
                    showProgressBar.setTitle("复制中");
                    showProgressBar.setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - 120,
                            (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - 40, 240, 80);
                    showProgressBar.setVisible(true);
    
                    new Thread(
                            () ->
                            {
                                while (getCopyPercent() < 100)
                                {
                                    try
                                    {
                                        Thread.sleep(400);
                                    }
                                    catch(InterruptedException e)
                                    {
                                        e.printStackTrace();
                                    }
                                    if(index < text.length)
                                        textField.setText("复制中"+text[index++]);
                                    else
                                        index = 0;
                                }
                            }
                    ).start();
                }
            }
        }
    
        private static void finish(long start)
        {
            long end = System.currentTimeMillis();
            showProgressBar.dispose();
            showMessage("复制成功,用时:" + (end - start) / 1000.0 + "s");
    
            copyProgressBar.setVisible(false);
            showProgressBar.remove(copyProgressBar);
            textField.setVisible(false);
            showProgressBar.remove(textField);
    
            Arrays.fill(traverseMethod, false);
            Arrays.fill(copyMethod, false);
            des = src = null;
            desSize = srcSize;
        }
    }
    
  • 相关阅读:
    c/cpp枚举练习
    数据类型的标识
    引用变量
    cocos2dx 3.3 笔记
    希望获取到页面中所有的checkbox怎么做?
    如何判断某变量是否为数组数据类型?
    驼峰函数写法
    trim()函数
    js 获取页面可视区域宽高
    全屏滚动插件
  • 原文地址:https://www.cnblogs.com/6b7b5fc3/p/12715781.html
Copyright © 2020-2023  润新知