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

1.背景

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

  • 传统的字节流读写复制FileInputStream,FileOutputStream,BufferedInputStream,BufferedOutputStream
  • 传统的字符流读写复制FileReader,FileWriter,BufferWriter,BufferedWriter,BufferedReader
  • NIO系列的FileChannel
  • FileChannel+缓冲
  • java.nio.Files.copy()
  • 第三方包中的FileUtils.copy方法,比如org.apache.commons.io.FileUtils,org.codehaus.plexus.util.FileUtils等等.

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

2.概述

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

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

复制的8种方法:

  • (1)FileInputStream+FileOutputStream
  • (2)BufferedInputStream+BufferedOutputStream
  • (3)FileReader+FileWriter
  • (4)BufferedReader+BufferedWriter
  • (5)FileChannel
  • (6)FileChannel+buffer
  • (7)org.apache.commons.io.FileUtils.copyFile()
  • (8)java.nio.file.Files.copy()

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

3.jar包

1.org.apache.commons

2.org.codehaus.plexus

4.遍历

(1)listFiles()

1 private static void traverseByListFiles(File srcFile,File desFile) throws IOException 2{ 3 if(srcFile.isDirectory()) 4 { 5 File[] files = srcFile.listFiles(); 6 assert files != null; 7 for(File file : files) 8 { 9 File desFileOrDir = new File(desFile.getAbsolutePath() + File.separator + file.getName()); 10 if(file.isDirectory()) 11 { 12 if(desFileOrDir.exists()) 13 desFileOrDir.delete(); 14 desFileOrDir.mkdirs(); 15 } 16 traverseByListFiles(file, desFileOrDir); 17 } 18 } 19 else 20 { 21 copyFile(srcFile, desFile); 22 } 23}

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

(2)list()

1private static void traverseByList(File srcFile,File desFile) throws IOException 2{ 3 if (srcFile.isDirectory()) 4 { 5 String[] files = srcFile.list(); 6 assert files != null; 7 for (String file : files) 8 { 9 File subSrcFile = new File(srcFile, file); 10 File subDesFile = new File(desFile, file); 11 if (subSrcFile.isDirectory()) 12 { 13 if (subDesFile.exists()) 14 subDesFile.delete(); 15 subDesFile.mkdirs(); 16 } 17 traverseByList(subSrcFile, subDesFile); 18 } 19 } 20 else 21 { 22 copyFile(srcFile, desFile); 23 } 24}

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

(3)org.codehaus.plexus.util.FileUtils.getFiles

1private static void traverseByGetFiles(File srcFile, File desFile) throws IOException 2{ 3 if (srcFile.isDirectory()) 4 { 5 java.util.List<File> fileList = org.codehaus.plexus.util.FileUtils.getFiles(srcFile,null,null); 6 for (File file : fileList) 7 { 8 File desFileOrDir = new File(desFile.getAbsolutePath() + File.separator + file.getName()); 9 if(file.isDirectory()) 10 { 11 if(desFileOrDir.exists()) 12 desFileOrDir.delete(); 13 desFileOrDir.mkdirs(); 14 } 15 traverseByListFiles(file, desFileOrDir); 16 } 17 } 18 else 19 { 20 copyFile(srcFile, desFile); 21 } 22}

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

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

返回的结果的java.util.List

(4)Commons.io工具包

1private static void traverseByCommonsIO(File srcFile, File desFile) throws IOException 2{ 3 if (srcFile.isDirectory()) 4 { 5 Collection<File> files = org.apache.commons.io.FileUtils.listFiles(srcFile,null,false); 6 for (File file : files) 7 { 8 File desFileOrDir = new File(desFile.getAbsolutePath() + File.separator + file.getName()); 9 if(file.isDirectory()) 10 { 11 if(desFileOrDir.exists()) 12 desFileOrDir.delete(); 13 desFileOrDir.mkdirs(); 14 } 15 traverseByCommonsIO(file, desFileOrDir); 16 } 17 } 18 else 19 { 20 copyFile(srcFile, desFile); 21 } 22}

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

(5)NIO--walkFileTree

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

1private static void traverseByNIO2(File srcFile) throws IOException 2{ 3 java.nio.file.Files.walkFileTree(srcFile.toPath(), new SimpleFileVisitor<>() { 4 @Override 5 public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException 6 { 7 File d = new File(des.toString() + path.toAbsolutePath().toString().substring(src.toString().length())); 8 new File(d.toString().substring(0, d.toString().lastIndexOf(File.separator))).mkdirs(); 9 copyFile(path.toFile(), d); 10 return FileVisitResult.CONTINUE; 11 } 12 }); 13}

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

1public interface FileVisitor<T> 2{ 3 FileVisitResult preVisitDirectory(T dir,BasicFileAttributes attrs) 4 { 5 //访问dir前的操作,dir类型一般为java.nio.Path 6 } 7 8 FileVisitResult postVisitDirectory(T dir,BasicFileAttributes attrs) 9 { 10 //访问dir后的操作 11 } 12 13 FileVisitResult visitFile(T file,BasicFileAttributes attrs) 14 { 15 //访问file时的操作 16 } 17 18 FileVisitResult visitFileFailed(T file,BasicFileAttributes attrs) 19 { 20 //访问file失败时的操作 21 } 22}

在上面的例子中只是实现了visitFile,因为只是复制操作,首先判断是否是源目录的路径,不是的话创建文件夹再复制文件. 这里说一下返回值FileVisitResult.FileVisitResult是一个枚举类型,根据返回值判断是否继续遍历. FileVisitResult可取值:

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

5.复制

(1)FileInputStream+FileOutputStream

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

1private static void copyByFileStream(File srcFile,File desFile) throws IOException 2{ 3 FileInputStream inputStream = new FileInputStream(srcFile); 4 FileOutputStream outputStream = new FileOutputStream(desFile); 5 byte [] b = new byte[1024]; 6 while(inputStream.read(b) != -1) 7 { 8 outputStream.write(b); 9 addCopySize(); 10 } 11 inputStream.close(); 12 outputStream.close(); 13}

这里说一下三个read方法的区别,FileInputStream有三个read方法:

1input.read(); 2input.read(b); 3input.read(b,off,len);

A.read()

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

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

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

B.read(b)

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

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

C.read(b,off,len)

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

1byte [] b = new byte[1024]; 2int n; 3while((n = input.read(b,0,1024))!=-1) 4 output.write(b,0,n); 5 6 7public int read(byte b[], int off, int len) throws IOException 8{ 9 return readBytes(b, off, len); 10} 11 12public int read(byte b[]) throws IOException 13{ 14 return readBytes(b, 0, b.length); 15}

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

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

至于效率...可以看看结果(作者用的是10G内的小文件):

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

可以看到,没有哪个一定比另外一个更快(不过最后一个误差有点太大了?7G不够的文件.). 采用哪一个建议自己去测试,毕竟这存在很多误差,比如文件,java版本,机器本身等等,仅供参考.

(2)BufferedInputStream+BufferedOutputStream

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

1private static void copyByBufferStream(File srcFile,File desFile) throws IOException 2{ 3 BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(srcFile)); 4 BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(desFile)); 5 byte [] b = new byte[1024]; 6 while(inputStream.read(b) != -1) 7 { 8 addCopySize(); 9 outputStream.write(b); 10 } 11 inputStream.close(); 12 outputStream.close(); 13}

这里也说一下BufferedInputStream的三个read(实际上还有,还有readN,与read(),read()肯定最慢,readN作者很少用,所以就没列出来了)

1read(b); 2read(b,off,len); 3readAllBytes();

A.read(b)

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

B.read(b,off,len)

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

C.readAllBytes()

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

output.write(input.readAllBytes());

但是呢,有代价的:

在这里插入图片描述

会出现OutOfMemory错误,就是对于大文件还是老老实实分开吧,不要"一口搞定","多吃几口".

看看效率:

在这里插入图片描述

readAllBytes对于大文件(作者这个是5G内的文件)直接爆内存....

在这里插入图片描述

在这里插入图片描述

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

(3)FileReader+FileWriter

字符流读写FileReader+FileWriter,相比起字节流的read,基本上把byte[]换成char[]即可,因为是逐个字符读取,而字节流是逐个字节读取因此采用byte[]. 注意这个不能用来读取图片,音乐等文件,不然复制出来的文件打不开.

1private static void copyByFileReader(File srcFile,File desFile) throws IOException 2{ 3 FileReader reader = new FileReader(srcFile); 4 FileWriter writer = new FileWriter(desFile); 5 6 char [] c = new char[1024]; 7 while(reader.read(c) != -1) 8 { 9 addCopySize(); 10 writer.write(c); 11 } 12 reader.close(); 13 writer.close(); 14}

(4)BufferedReader+BufferedWriter

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

1private static void copyByBufferReader(File srcFile,File desFile) throws IOException 2{ 3 BufferedReader reader = new BufferedReader(new FileReader(srcFile)); 4 BufferedWriter writer = new BufferedWriter(new FileWriter(desFile)); 5 6 char [] c = new char[1024]; 7 while(reader.read(c) != -1) 8 { 9 addCopySize(); 10 writer.write(c); 11 } 12 reader.close(); 13 writer.close(); 14}

(5)NIO--FileChannel

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

1private static void copyByFileChannel(File srcFile,File desFile) throws IOException 2{ 3 FileChannel srcChannel = new FileInputStream(srcFile).getChannel(); 4 FileChannel desChannel = new FileOutputStream(desFile).getChannel(); 5 srcChannel.transferTo(0,srcChannel.size(),desChannel); 6 srcChannel.close(); 7 desChannel.close(); 8}

(6)NIO--FileChannel+ByteBuffer

在利用了FileInputStream与FileOutputStream打开了FileChannel的基础上,配合ByteBuffer使用.

1private static void copyByFileChannelWithBuffer(File srcFile,File desFile) throws IOException 2{ 3 FileChannel srcChannel = new FileInputStream(srcFile).getChannel(); 4 FileChannel desChannel = new FileOutputStream(desFile).getChannel(); 5 ByteBuffer buffer = ByteBuffer.allocateDirect(1024); 6 while(srcChannel.read(buffer) != -1) 7 { 8 buffer.flip(); 9 desChannel.write(buffer); 10 buffer.clear(); 11 addCopySize(); 12 } 13 srcChannel.close(); 14 desChannel.close(); 15}

flip的意思是"翻转",

buffer.flip();

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

看看这两种方法效率:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

另外作者发现transferTo的"上限"为2G,就是对于大于2G的单个文件最多最能复制2个G.

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

(7)FileUtils.copyFile()

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

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

(8)Files.copy()

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

1StandardCopyOption.REPLACE_EXISTING 2 3 4private static void copyByFiles(File srcFile,File desFile) throws IOException 5{ 6 Files.copy(srcFile.toPath(), desFile.toPath(), StandardCopyOption.REPLACE_EXISTING); 7}

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

6.其他

(1)swing布局

A.网格布局

主JFrame采用了网格布局

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

三行一列,因为只要三个按钮,选择源文件(夹),选择目标文件夹,选择遍历方式. 选择遍历方式/复制方式的JFrame同样适用了网格布局:

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

B.居中

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

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

C.组件的添加与删除

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

1traverseMethodButton.setVisible(false); 2remove(traverseMethodButton); 3add(copyMethodButton); 4copyMethodButton.setVisible(true);

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

(2)进度条

进度条这个东西把作者搞得很惨啊......其实就是新建一个线程就可以了. 核心代码为:

1new Thread( 2 () -> 3 { 4 int percent; 5 while ((percent = getCopyPercent()) < 100) 6 { 7 try 8 { 9 Thread.sleep(100); 10 } 11 catch(InterruptedException e) 12 { 13 e.printStackTrace(); 14 } 15 copyProgressBar.setValue(percent); 16 } 17 } 18).start();

作者的JProgressBar是直接添加在一个JFrame中的,不用什么太复杂的布局. 获取百分比后调用setValue(),一定要新建一个线程操作,不然不能正常显示进度条. 另外复制的操作建议使用SwingWorker.

1SwingWorker<String,Object> copyTask = new SwingWorker<>() 2{ 3 @Override 4 protected String doInBackground() 5 { 6 try 7 { 8 if (traverseMethod[0]) 9 traverseByListFiles(src, des); 10 else if (traverseMethod[1]) 11 traverseByList(src, des); 12 else if (traverseMethod[2]) 13 traverseByGetFiles(src, des); 14 else if (traverseMethod[3]) 15 traverseByCommonsIO(src, des); 16 else if (traverseMethod[4]) 17 traverseByNIO2(src); 18 else 19 { 20 showProgressBar.dispose(); 21 showMessage("遍历失败,找不到遍历方法"); 22 } 23 } 24 catch (IOException e) 25 { 26 e.printStackTrace(); 27 showProgressBar.dispose(); 28 showMessage("未知错误复制失败"); 29 } 30 finish(start); 31 return null; 32 } 33}; 34copyTask.execute();

7.测试

说了那么多来点实际的. (以下所有的测试都是删除复制的文件后再进行新一次的复制.)

(1)1G文件

1G file

File.listFiles()

File.list()

plexus.util.FileUtils.getFiles()

commons.io.FileUtils.listFiles

Files.walkFileTree

FileIntput/OutputStream

20.189s

21.152s

18.249s

20.131s

21.782s

BufferedInput/OuputStream

17.761s

23.786s

22.118s

19.646s

16.806s

FileReader/Writer

61.334s

58.3s

58.904s

58.679s

55.762s

BufferedReader/Writer

63.287s

59.546s

56.664s

58.212s

59.884s

FileChannel

20.097s

22.272s

22.751s

22.765s

20.291s

FileChannel+ByteBuffer

18.857s

22.489s

23.148s

22.337s

17.213s

FileUtils.copyFile

25.398s

21.95s

22.808s

25.325s

22.483s

Files.copy

16.272s

14.166s

17.057s

14.987s

10.653s

文件的话其实纵向比较即可,因为基本不用怎么遍历,横向比较可以勉强看作求平均值. 对于非文本文件,FileReader/Writer和BufferedReader/Writer没有太大的参考意义,比如复制视频文件是打不开的,而且复制出来的文件会变大.对于单文件Files.copy的性能非常好,java的nio果然厉害.

(2)10G文件

10G file

File.listFiles()

File.list()

plexus.util.FileUtils.getFiles()

commons.io.FileUtils.listFiles

Files.walkFileTree

FileIntput/OutputStream

171.427s

173.146s

172.611s

184.182s

250.251s

BufferedInput/OuputStream

203.509s

174.792s

167.727s

177.451s

217.53s

FileReader/Writer

187.55s

169.306s

226.571s

168.982s

218.303s

BufferedReader/Writer

155.134s

165.883s

166.192s

176.488s

206.306s

FileChannel

34.48s

35.445s

43.896s

41.827s

41.755s

FileChannel+ByteBuffer

175.632s

167.091s

178.455s

182.977s

183.763s

FileUtils.copyFile

203.997s

206.623s

201.01s

213.949s

208.739s

Files.copy

209.898s

186.889s

244.355s

222.336s

244.68s

这个10G的文件是文本文件. 现在可以看看FileChannel的这一行,明显所花的时间要比其他要少,为什么呢? 因为文件大于2G.FileChannel的trasferTo方法只能写入最多2G的文件,所以对于大于2G的文件复制出来只有2G,因此FileChannel的这一行没有太大可比性.对于文本文件,BufferedReader/Writer的复制速度是最快的了,其次是FileInput/OutputStream.对于单个大文件,apache的FileUtils与NIO的Files.copy的速度比FileInputStream慢啊...

(3)1G目录

1G dir

File.listFiles()

File.list()

plexus.util.FileUtils.getFiles()

commons.io.FileUtils.listFiles

Files.walkFileTree

FileIntput/OutputStream

23.549s

99.386s

143.388s

13.451s

10.773s

BufferedInput/OuputStream

6.306s

59.458s

20.704s

6.668s

6.616s

FileReader/Writer

49.059s

103.257s

51.995s

49.729s

51.509s

BufferedReader/Writer

59.932s

127.359s

51.731s

51.418s

50.317s

FileChannel

40.082s

71.713s

17.617s

15.782s

19.777s

FileChannel+ByteBuffer

33.355s

83.845s

19.68s

10.288s

17.152s

FileUtils.copyFile

24.163s

63.979s

8.277s

6.115s

19.513s

Files.copy

14.528s

28.215s

6.578s

5.883s

7.502s

对于目录的话可以考虑放弃BufferedReader与FileReader了,除非全部是文本文件,否则推荐使用BufferedInput/OutputStream与Files.copy()进行复制,工具类FileUtils的复制方法表现还是不错的,但相比起java标准的Files.copy效率都差了. 对于FileChannel与配合缓冲使用的FileChannel,1G的话好像不相上下. 遍历方式的话...可以看到plexus的遍历方法表现差距很大,而apache的listFiles或者java nio的walkFileTree比较稳定且速度还可以,推荐使用这两种方式遍历目录.

(4)10G目录

10G dir

File.listFiles()

File.list()

plexus.util.FileUtils.getFiles()

commons.io.FileUtils.listFiles

Files.walkFileTree

FileIntput/OutputStream

216.822s

228.792s

227.908s

240.042s

191.863s

BufferedInput/OuputStream

218.599s

210.941s

207.375s

213.991s

167.614s

FileReader/Writer

536.747s

550.755s

550.415s

548.881s

516.684s

BufferedReader/Writer

587.612s

552.55s

549.716s

553.484s

498.18s

FileChannel

115.126s

117.538s

117.456s

118.207s

97.626s

FileChannel+ByteBuffer

225.887s

224.932s

222.077s

223.812s

180.177s

FileUtils.copyFile

233.724s

230.199s

232.133s

223.286s

189.737s

Files.copy

229.819s

227.562s

226.793s

226.78s

181.071s

FileReader与BufferedReader这两行可以忽略了.对于小文件用FileChannel的话还是不错的,对于大文件一定要用FileChannel的话可以配合ByteBuffer使用,不过从数据上看效果比BufferedInput/OutputStream要低. 再看看org.apache.commons.io.FileUtils与java.nio.file.Files的copy,差别不太,效果接近,但在1G的时候差距有点大. 遍历方式的话,java nio的walkFileTrees最快.

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

8.源码

作者比较偷懒全部仍在一个文件了.七百行.

1import java.awt.*; 2import javax.swing.*; 3import java.nio.*; 4import java.nio.channels.*; 5import java.io.*; 6import java.nio.file.*; 7import java.nio.file.attribute.*; 8import java.util.*; 9import org.apache.commons.io.*; 10 11public class Test extends JFrame 12{ 13 public static final long serialVersionUID = 12398129389122L; 14 15 private JFrame showTraverseMethod = new JFrame("遍历方式"); 16 private JFrame showCopyMethod = new JFrame("复制方式"); 17 18 private JButton traverseMethodButton = new JButton("请选择遍历方式"); 19 private JButton copyMethodButton = new JButton("请选择复制方式"); 20 private JButton copyButton = new JButton("开始复制"); 21 22 private JButton traverseByListFiles = new JButton("File.listFiles()"); 23 private JButton traverseByList = new JButton("File.list()"); 24 private JButton traverseByGetFiles = new JButton("(plexus)getFiles()"); 25 private JButton traverseByCommonsIO = new JButton("Commons IO"); 26 private JButton traverseByNIO2 = new JButton("NIO2"); 27 28 private JButton copyByFileStream = new JButton("File stream"); 29 private JButton copyByBufferStream = new JButton("Buffer stream"); 30 private JButton copyByFileReader = new JButton("File reader"); 31 private JButton copyByBufferReader = new JButton("Buffer reader"); 32 private JButton copyByFileChannel = new JButton("File channel"); 33 private JButton copyByFileChannelWithBuffer = new JButton("File channel with buffer"); 34 private JButton copyByCommonsIO = new JButton("Commons IO"); 35 private JButton copyByFiles = new JButton("Files.copy"); 36 37 public Test() 38 { 39 JButton src = new JButton("选择源文件(夹)"); 40 src.addActionListener( 41 event -> 42 { 43 JFileChooser fileChooser = new JFileChooser(); 44 fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); 45 fileChooser.showDialog(new Label(), "选择文件(夹)"); 46 FilesCopy.setSrc(fileChooser.getSelectedFile()); 47 } 48 ); 49 JButton des = new JButton("选择目标文件夹"); 50 des.addActionListener( 51 event -> 52 { 53 JFileChooser fileChooser = new JFileChooser(); 54 fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); 55 fileChooser.showDialog(new JLabel(),"选择文件夹"); 56 FilesCopy.setDes(fileChooser.getSelectedFile()); 57 } 58 ); 59 60 traverseMethodButton.addActionListener( 61 event -> 62 { 63 traverseByListFiles.addActionListener( 64 e-> 65 { 66 FilesCopy.setTraverseByListFiles(); 67 showTraverseMethod.dispose(); 68 } 69 ); 70 71 traverseByList.addActionListener( 72 e -> 73 { 74 FilesCopy.setTraverseByList(); 75 showTraverseMethod.dispose(); 76 } 77 ); 78 79 traverseByGetFiles.addActionListener( 80 e -> 81 { 82 FilesCopy.setTraverseByGetfiles(); 83 showTraverseMethod.dispose(); 84 } 85 ); 86 87 traverseByCommonsIO.addActionListener( 88 e -> 89 { 90 FilesCopy.setTraverseByCommonsIO(); 91 showTraverseMethod.dispose(); 92 } 93 ); 94 95 traverseByNIO2.addActionListener( 96 e -> 97 { 98 FilesCopy.setTraverseByNIO2(); 99 showTraverseMethod.dispose(); 100 } 101 ); 102 103 104 showTraverseMethod.setLayout(new GridLayout(5,1,3,3)); 105 showTraverseMethod.setTitle("遍历方式"); 106 showTraverseMethod.setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - 200, 107 (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - 200, 400, 400); 108 showTraverseMethod.setVisible(true); 109 showTraverseMethod.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 110 111 showTraverseMethod.add(traverseByListFiles); 112 showTraverseMethod.add(traverseByList); 113 showTraverseMethod.add(traverseByGetFiles); 114 showTraverseMethod.add(traverseByCommonsIO); 115 showTraverseMethod.add(traverseByNIO2); 116 117 traverseMethodButton.setVisible(false); 118 remove(traverseMethodButton); 119 add(copyMethodButton); 120 copyMethodButton.setVisible(true); 121 } 122 ); 123 124 copyMethodButton.addActionListener( 125 event -> 126 { 127 copyByFileStream.addActionListener( 128 e -> 129 { 130 FilesCopy.setCopyByFileStream(); 131 showCopyMethod.dispose(); 132 } 133 ); 134 135 copyByBufferStream.addActionListener( 136 e -> 137 { 138 FilesCopy.setCopyByBufferStream(); 139 showCopyMethod.dispose(); 140 } 141 ); 142 143 copyByFileReader.addActionListener( 144 e -> 145 { 146 FilesCopy.setCopyByFileReader(); 147 showCopyMethod.dispose(); 148 } 149 ); 150 151 copyByBufferReader.addActionListener( 152 e -> 153 { 154 FilesCopy.setCopyByBufferReader(); 155 showCopyMethod.dispose(); 156 } 157 ); 158 159 copyByFileChannel.addActionListener( 160 e -> 161 { 162 FilesCopy.setCopyByFileChannel(); 163 showCopyMethod.dispose(); 164 } 165 ); 166 167 copyByFileChannelWithBuffer.addActionListener( 168 e -> 169 { 170 FilesCopy.setCopyByFileChannelWithBuffer(); 171 showCopyMethod.dispose(); 172 } 173 ); 174 175 copyByCommonsIO.addActionListener( 176 e -> 177 { 178 FilesCopy.setCopyByCommonsIO(); 179 showCopyMethod.dispose(); 180 } 181 ); 182 183 copyByFiles.addActionListener( 184 e -> 185 { 186 FilesCopy.setCopyByFiles(); 187 showCopyMethod.dispose(); 188 } 189 ); 190 191 showCopyMethod.setLayout(new GridLayout(4,2,5,5)); 192 showCopyMethod.setTitle("复制方式"); 193 showCopyMethod.setBounds( 194 (int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - 200, 195 (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - 200, 400, 400); 196 showCopyMethod.setVisible(true); 197 showCopyMethod.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 198 showCopyMethod.add(copyByFileStream); 199 showCopyMethod.add(copyByBufferStream); 200 showCopyMethod.add(copyByFileReader); 201 showCopyMethod.add(copyByBufferReader); 202 showCopyMethod.add(copyByFileChannel); 203 showCopyMethod.add(copyByFileChannelWithBuffer); 204 showCopyMethod.add(copyByCommonsIO); 205 showCopyMethod.add(copyByFiles); 206 207 copyMethodButton.setVisible(false); 208 remove(copyMethodButton); 209 add(copyButton); 210 copyButton.setVisible(true); 211 } 212 ); 213 214 copyButton.addActionListener( 215 event -> 216 { 217 if(FilesCopy.haveSelectedSrcAndDes()) 218 { 219 FilesCopy.copy(); 220 copyButton.setVisible(false); 221 remove(copyButton); 222 add(traverseMethodButton); 223 traverseMethodButton.setVisible(true); 224 } 225 else 226 JOptionPane.showMessageDialog(null,"请先选择源文件(夹)与目标文件夹!"); 227 } 228 ); 229 230 setLayout(new GridLayout(3,1,5,3)); 231 setTitle("复制文件"); 232 setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - 200, 233 (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - 200, 400, 400); 234 setVisible(true); 235 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 236 237 add(src); 238 add(des); 239 add(traverseMethodButton); 240 } 241 242 public static void main(String[] args) { 243 new Test(); 244 } 245} 246 247class FilesCopy 248{ 249 private static File src = null; 250 private static File des = null; 251 private static long desSize = 0; 252 private static long srcSize = 0; 253 private static boolean [] traverseMethod = {false,false,false,false,false,false}; 254 private static boolean[] copyMethod = { false, false, false, false, false, false ,false,false}; 255 private static JFrame showProgressBar = new JFrame(); 256 private static JProgressBar copyProgressBar = new JProgressBar(); 257 private static JTextField textField = new JTextField(); 258 private static int index = 0; 259 260 private static int getCopyPercent() 261 { 262 return (int)(desSize * 100.0 / srcSize); 263 } 264 265 private static void addCopySize() { 266 desSize += 1024L; 267 } 268 269 public static void setTraverseByListFiles() 270 { 271 traverseMethod[0] = true; 272 } 273 274 private static void traverseByListFiles(File srcFile,File desFile) throws IOException 275 { 276 if(srcFile.isDirectory()) 277 { 278 File[] files = srcFile.listFiles(); 279 assert files != null; 280 for(File file : files) 281 { 282 File desFileOrDir = new File(desFile.getAbsolutePath() + File.separator + file.getName()); 283 if(file.isDirectory()) 284 { 285 if(desFileOrDir.exists()) 286 desFileOrDir.delete(); 287 desFileOrDir.mkdirs(); 288 } 289 traverseByListFiles(file, desFileOrDir); 290 } 291 } 292 else { 293 copyFile(srcFile, desFile); 294 } 295 } 296 297 public static void setTraverseByList() 298 { 299 traverseMethod[1] = true; 300 } 301 302 private static void traverseByList(File srcFile,File desFile) throws IOException 303 { 304 if (srcFile.isDirectory()) 305 { 306 String[] files = srcFile.list(); 307 assert files != null; 308 for (String file : files) 309 { 310 File subSrcFile = new File(srcFile, file); 311 File subDesFile = new File(desFile, file); 312 if (subSrcFile.isDirectory()) 313 { 314 if (subDesFile.exists()) 315 subDesFile.delete(); 316 subDesFile.mkdirs(); 317 } 318 traverseByList(subSrcFile, subDesFile); 319 } 320 } 321 else 322 { 323 copyFile(srcFile, desFile); 324 } 325 326 } 327 328 public static void setTraverseByGetfiles() 329 { 330 traverseMethod[2] = true; 331 } 332 333 private static void traverseByGetFiles(File srcFile, File desFile) throws IOException 334 { 335 if (srcFile.isDirectory()) 336 { 337 java.util.List<File> fileList = org.codehaus.plexus.util.FileUtils.getFiles(srcFile,null,null); 338 for (File file : fileList) 339 { 340 File desFileOrDir = new File(desFile.getAbsolutePath() + File.separator + file.getName()); 341 if(file.isDirectory()) 342 { 343 if(desFileOrDir.exists()) 344 desFileOrDir.delete(); 345 desFileOrDir.mkdirs(); 346 } 347 traverseByListFiles(file, desFileOrDir); 348 } 349 } 350 else 351 { 352 copyFile(srcFile, desFile); 353 } 354 } 355 356 public static void setTraverseByCommonsIO() 357 { 358 traverseMethod[3] = true; 359 } 360 361 private static void traverseByCommonsIO(File srcFile, File desFile) throws IOException 362 { 363 if (srcFile.isDirectory()) 364 { 365 Collection<File> files = org.apache.commons.io.FileUtils.listFiles(srcFile,null,false); 366 for (File file : files) 367 { 368 File desFileOrDir = new File(desFile.getAbsolutePath() + File.separator + file.getName()); 369 if(file.isDirectory()) 370 { 371 if(desFileOrDir.exists()) 372 desFileOrDir.delete(); 373 desFileOrDir.mkdirs(); 374 } 375 traverseByCommonsIO(file, desFileOrDir); 376 } 377 } 378 else { 379 copyFile(srcFile, desFile); 380 } 381 } 382 383 public static void setTraverseByNIO2() 384 { 385 traverseMethod[4] = true; 386 } 387 388 private static void traverseByNIO2(File srcFile) throws IOException 389 { 390 java.nio.file.Files.walkFileTree(srcFile.toPath(), new SimpleFileVisitor<>() { 391 @Override 392 public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { 393 File d = new File(des.toString() + path.toAbsolutePath().toString().substring(src.toString().length())); 394 new File(d.toString().substring(0, d.toString().lastIndexOf(File.separator))).mkdirs(); 395 copyFile(path.toFile(), d); 396 return FileVisitResult.CONTINUE; 397 } 398 }); 399 } 400 401 public static void setCopyByFileStream() 402 { 403 copyMethod[0] = true; 404 } 405 406 private static void copyByFileStream(File srcFile,File desFile) throws IOException 407 { 408 FileInputStream inputStream = new FileInputStream(srcFile); 409 FileOutputStream outputStream = new FileOutputStream(desFile); 410 byte [] b = new byte[1024]; 411 while(inputStream.read(b) != -1) 412 { 413 outputStream.write(b); 414 addCopySize(); 415 } 416 inputStream.close(); 417 outputStream.close(); 418 } 419 420 public static void setCopyByBufferStream() 421 { 422 copyMethod[1] = true; 423 } 424 425 private static void copyByBufferStream(File srcFile,File desFile) throws IOException 426 { 427 BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(srcFile)); 428 BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(desFile)); 429 byte [] b = new byte[1024]; 430 while(inputStream.read(b) != -1) 431 { 432 addCopySize(); 433 outputStream.write(b); 434 } 435 inputStream.close(); 436 outputStream.close(); 437 } 438 439 public static void setCopyByFileReader() 440 { 441 copyMethod[2] = true; 442 } 443 444 private static void copyByFileReader(File srcFile,File desFile) throws IOException 445 { 446 FileReader reader = new FileReader(srcFile); 447 FileWriter writer = new FileWriter(desFile); 448 449 char [] c = new char[1024]; 450 while(reader.read(c) != -1) 451 { 452 addCopySize(); 453 writer.write(c); 454 } 455 reader.close(); 456 writer.close(); 457 } 458 459 public static void setCopyByBufferReader() 460 { 461 copyMethod[3] = true; 462 } 463 464 private static void copyByBufferReader(File srcFile,File desFile) throws IOException 465 { 466 BufferedReader reader = new BufferedReader(new FileReader(srcFile)); 467 BufferedWriter writer = new BufferedWriter(new FileWriter(desFile)); 468 469 char [] c = new char[1024]; 470 while(reader.read(c) != -1) 471 { 472 addCopySize(); 473 writer.write(c); 474 } 475 reader.close(); 476 writer.close(); 477 } 478 479 public static void setCopyByFileChannel() 480 { 481 copyMethod[4] = true; 482 } 483 484 private static void copyByFileChannel(File srcFile,File desFile) throws IOException 485 { 486 FileChannel srcChannel = new FileInputStream(srcFile).getChannel(); 487 FileChannel desChannel = new FileOutputStream(desFile).getChannel(); 488 srcChannel.transferTo(0,srcChannel.size(),desChannel); 489 srcChannel.close(); 490 desChannel.close(); 491 } 492 493 public static void setCopyByFileChannelWithBuffer() 494 { 495 copyMethod[5] = true; 496 } 497 498 private static void copyByFileChannelWithBuffer(File srcFile,File desFile) throws IOException 499 { 500 FileChannel srcChannel = new FileInputStream(srcFile).getChannel(); 501 FileChannel desChannel = new FileOutputStream(desFile).getChannel(); 502 ByteBuffer buffer = ByteBuffer.allocateDirect(1024); 503 while(srcChannel.read(buffer) != -1) 504 { 505 buffer.flip(); 506 desChannel.write(buffer); 507 buffer.clear(); 508 addCopySize(); 509 } 510 srcChannel.close(); 511 desChannel.close(); 512 } 513 514 public static void setCopyByCommonsIO() 515 { 516 copyMethod[6] = true; 517 } 518 519 private static void copyByCommonsIO(File srcFile,File desFile) throws IOException 520 { 521 FileUtils.copyFile(srcFile, desFile); 522 } 523 524 public static void setCopyByFiles() 525 { 526 copyMethod[7] = true; 527 } 528 529 private static void copyByFiles(File srcFile,File desFile) throws IOException 530 { 531 Files.copy(srcFile.toPath(), desFile.toPath(), StandardCopyOption.REPLACE_EXISTING); 532 } 533 534 public static void setSrc(File srcFile) { 535 src = srcFile; 536 if(srcFile.isDirectory()) 537 srcSize = org.apache.commons.io.FileUtils.sizeOfDirectory(srcFile); 538 else 539 srcSize = src.length(); 540 } 541 542 public static void setDes(File desFile) { 543 des = desFile; 544 desSize = 0; 545 } 546 547 public static void setSrc(Path srcPath) 548 { 549 setSrc(srcPath.toFile()); 550 } 551 552 public static void setDes(Path desPath) 553 { 554 setDes(desPath.toFile()); 555 } 556 557 private static void copyFile(File srcFile,File desFile) throws IOException 558 { 559 if (copyMethod[0]) 560 copyByFileStream(srcFile,desFile); 561 else if (copyMethod[1]) 562 copyByBufferStream(srcFile, desFile); 563 else if (copyMethod[2]) 564 copyByFileReader(srcFile, desFile); 565 else if (copyMethod[3]) 566 copyByBufferReader(srcFile, desFile); 567 else if (copyMethod[4]) 568 copyByFileChannel(srcFile, desFile); 569 else if (copyMethod[5]) 570 copyByFileChannelWithBuffer(srcFile, desFile); 571 else if (copyMethod[6]) 572 copyByCommonsIO(srcFile, desFile); 573 else if (copyMethod[7]) 574 copyByFiles(srcFile, desFile); 575 else 576 showMessage("复制失败,找不到复制方法."); 577 } 578 579 private static void showMessage(String message) 580 { 581 JOptionPane.showMessageDialog(null, message); 582 } 583 584 public static boolean haveSelectedSrcAndDes() 585 { 586 return src != null && des != null; 587 } 588 589 public static void copy() 590 { 591 long start = System.currentTimeMillis(); 592 if(haveSelectedSrcAndDes()) 593 { 594 if(src.isFile()) 595 { 596 des = new File(des.getAbsolutePath()+File.separator+src.getName()); 597 } 598 SwingWorker<String,Object> copyTask = new SwingWorker<>() 599 { 600 @Override 601 protected String doInBackground() 602 { 603 try 604 { 605 if (traverseMethod[0]) 606 traverseByListFiles(src, des); 607 else if (traverseMethod[1]) 608 traverseByList(src, des); 609 else if (traverseMethod[2]) 610 traverseByGetFiles(src, des); 611 else if (traverseMethod[3]) 612 traverseByCommonsIO(src, des); 613 else if (traverseMethod[4]) 614 traverseByNIO2(src); 615 else 616 { 617 showProgressBar.dispose(); 618 showMessage("遍历失败,找不到遍历方法"); 619 } 620 } 621 catch (IOException e) 622 { 623 e.printStackTrace(); 624 showProgressBar.dispose(); 625 showMessage("未知错误复制失败"); 626 } 627 finish(start); 628 return null; 629 } 630 }; 631 copyTask.execute(); 632 if (!copyMethod[4] && !copyMethod[6] && !copyMethod[7]) 633 { 634 copyProgressBar.setMinimum(0); 635 copyProgressBar.setMaximum(100); 636 copyProgressBar.setValue(0); 637 copyProgressBar.setVisible(true); 638 copyProgressBar.setStringPainted(true); 639 640 showProgressBar.add(copyProgressBar); 641 showProgressBar.setTitle("复制进度"); 642 showProgressBar.setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - 150, 643 (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - 50, 300, 100); 644 showProgressBar.setVisible(true); 645 new Thread( 646 () -> 647 { 648 int percent; 649 while ((percent = getCopyPercent()) < 100) 650 { 651 try 652 { 653 Thread.sleep(100); 654 } 655 catch(InterruptedException e) 656 { 657 e.printStackTrace(); 658 } 659 copyProgressBar.setValue(percent); 660 } 661 } 662 ).start(); 663 } 664 else 665 { 666 667 final String [] text = {".","..","...","....",".....",".......","......",".....","....","...","..","."}; 668 textField.setVisible(true); 669 textField.setHorizontalAlignment(JTextField.CENTER); 670 textField.setEditable(false); 671 showProgressBar.add(textField); 672 showProgressBar.setTitle("复制中"); 673 showProgressBar.setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - 120, 674 (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - 40, 240, 80); 675 showProgressBar.setVisible(true); 676 677 new Thread( 678 () -> 679 { 680 while (getCopyPercent() < 100) 681 { 682 try 683 { 684 Thread.sleep(400); 685 } 686 catch(InterruptedException e) 687 { 688 e.printStackTrace(); 689 } 690 if(index < text.length) 691 textField.setText("复制中"+text[index++]); 692 else 693 index = 0; 694 } 695 } 696 ).start(); 697 } 698 } 699 } 700 701 private static void finish(long start) 702 { 703 long end = System.currentTimeMillis(); 704 showProgressBar.dispose(); 705 showMessage("复制成功,用时:" + (end - start) / 1000.0 + "s"); 706 707 copyProgressBar.setVisible(false); 708 showProgressBar.remove(copyProgressBar); 709 textField.setVisible(false); 710 showProgressBar.remove(textField); 711 712 Arrays.fill(traverseMethod, false); 713 Arrays.fill(copyMethod, false); 714 des = src = null; 715 desSize = srcSize; 716 } 717}
点赞
收藏

评论区

加载中...

相关推荐

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java 复制Map对象(深拷贝与浅拷贝)

java复制Map对象(深拷贝与浅拷贝)CreationTime2018年6月4日10点00分Author:Marydon1.深拷贝与浅拷贝  浅拷贝:只复制对象的引用,两个引用仍然指向同一个对象

IO(输入输出)

IO流有很多种,按照操作数据的不同,可以分为字节流和字符流,按照数组传输方向的不同又可分为输入流和输出流。字节流的输入输出流分别用java.io.InputStream和java.io.OutputStream表示,字符流的输入输出流分别用java.io.Reader和java.io.Writer表示。!(https://oscimg

Java复制文件的4种方式

1\.使用FileStreams复制这是最经典的方式将一个文件的内容复制到另一个文件中。使用FileInputStream读取文件A的字节,使用FileOutputStream写入到文件B。这是第一个方法的代码:private static void copyFileUsingFileStreams(File source, F

MySQL Replication

支持的复制类型:1.基于语句的复制mysql默认采用基于语句的复制,效率高。基本方式:在master上执行的sql语句,在slave上再次执行同样的语句,而一旦发现没法复制时会自动选择基于行的复制。2.基于行的复制基本方式:把master服务器上改变的内容复制过去,而不是把sql语句在从服务器上执行一遍,mysql从5.0开始支持基于行的复制

Java NIO之缓冲区

JavaNIO之Buffer  Java传统的I/O模型是面向单个字节的,它将输入输出抽象为字节流或字符流。这种单个字节的读取或写入模型的效率比较低,而且不符合操作系统的I/O特点。操作系统的IO是面向字节块的,通常是直接从磁盘中读取一块数据到内存或写入一块数据到磁盘。JavaNIO提供了缓冲区来实现字节块的读写。