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种方法:
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);
}
}
通过srcFile
的listFiles()
获取所有的子文件与子文件夹,然后判断是否是目录:
- 如果是目录,首先判断有没有这个文件(有时候本来是文件夹但是却存在同名的文件,就先删除),再创建文件夹,然后递归执行函数
- 如果不是目录,直接把两个
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.FileUtils
的listFiles
方法,参数为要遍历的目录,一个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
,因为只是复制操作,首先判断是否是源目录的路径,不是的话创建文件夹再复制文件。
这里说一下返回值FileVisitResult
,FileVisitResult
是一个枚举类型,根据返回值判断是否继续遍历,可取值如下:
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
,相比起FileInputStream
,BufferedInputStream
读取时会先从缓冲区读取数据,缓冲区无可读数据再从文件读取,所以会比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
+BufferedWriter
,BufferedReader
相比起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
复制,首先通过FileInputStream
与FileOutputStream
打开流,再用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
在利用了FileInputStream
与FileOutputStream
打开了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/Writer
和BufferedReader/Writer
没有太大的参考意义,比如复制视频文件是打不开的,而且复制出来的文件会变大。对于单文件Files.copy
的性能非常好,NIO
果然厉害。
6.2 10G文件
这个10G的文件是文本文件。
现在可以看看FileChannel
的这一行,明显所花的时间要比其他要少,为什么呢?
因为文件大于2G,FileChannel
的trasferTo
方法只能写入最多2G的文件,所以对于大于2G的文件复制出来只有2G,因此FileChannel
的这一行没有太大可比性。对于文本文件,BufferedReader/Writer
的复制速度是最快的了,其次是FileInput/OutputStream
。对于单个大文件,FileUtils
与Files.copy
的速度比FileInputStream
慢。
6.3 1G目录
对于目录的话可以考虑放弃BufferedReader
与FileReader
了,除非全部是文本文件,否则推荐使用BufferedInput/OutputStream
与Files.copy()
进行复制,工具类FileUtils
的复制方法表现还是不错的,但相比起标准的Files.copy
效率差了。
对于FileChannel
与配合缓冲使用的FileChannel
,1G的话好像不相上下。
遍历方式的话...可以看到plexus
的遍历方法表现差距很大,而Apache
的listFiles
或者Java NIO
的walkFileTree
比较稳定且速度还可以,推荐使用这两种方式遍历目录。
6.4 10G目录
FileReader
与BufferedReader
这两行可以忽略了。对于小文件用FileChannel
的话还是不错的,对于大文件一定要用FileChannel
的话可以配合ByteBuffer
使用,不过从数据上看效果比BufferedInput/OutputStream
要低。
再看看org.apache.commons.io.FileUtils
与java.nio.file.Files
的复制,差别不太,效果接近,但在1G的时候差距有点大。
遍历方式的话,walkFileTrees
最快。
当然这些测试仅供参考,具体使用哪一个要看看具体环境,另外这种方式把遍历与复制分开,Apache
的FileUtils
有方法可以直接复制目录的,因此,使用哪个更合适还需要个人具体测试。
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;
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。