java復(fù)制文件的方式其實(shí)有很多種,可以分為
創(chuàng)新互聯(lián)建站服務(wù)項(xiàng)目包括保亭黎族網(wǎng)站建設(shè)、保亭黎族網(wǎng)站制作、保亭黎族網(wǎng)頁制作以及保亭黎族網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,保亭黎族網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到保亭黎族省份的部分城市,未來相信會繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
所以呢,看看各種方法效率怎么樣,主要衡量的標(biāo)準(zhǔn)就是時間,另外的一些標(biāo)準(zhǔn)包括大文件的復(fù)制時的內(nèi)存溢出等問題.
由于很多時候復(fù)制文件都包括了文件夾下的所有子目錄及文件的復(fù)制,所以作者采用的遍歷+復(fù)制方法去復(fù)制文件.就是把整個復(fù)制過程分為先遍歷,遍歷的過程中遇到文件夾就創(chuàng)建,遇到文件就調(diào)用不同的復(fù)制方法.
遍歷的5種方法:
復(fù)制的8種方法:
另外作者不太想看控制臺.....所以配合了一點(diǎn)swing使用.
1.org.apache.commons
2.org.codehaus.plexus
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()獲取所有的子文件與子文件夾,然后判斷是否是目錄
如果是目錄,首先判斷有沒有這個文件(有時候本來是文件夾但是卻存在同名的文件,就先刪除),再創(chuàng)建文件夾,然后遞歸執(zhí)行函數(shù).
如果不是目錄,直接把兩個File作為參數(shù)進(jìn)行文件復(fù)制,里面用什么方法后面會設(shè)置.
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[],也是先判斷目錄,創(chuàng)建目錄,不是目錄直接復(fù)制
private static void traverseByGetFiles(File srcFile, File desFile) throws IOException
{
if (srcFile.isDirectory())
{
java.util.List 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);
}
}
這是用了別人的工具類進(jìn)行遍歷.
org.codehaus.plexus.util.FileUtils.getFiles(srcFile,null,null);
返回的結(jié)果的java.util.List
private static void traverseByCommonsIO(File srcFile, File desFile) throws IOException
{
if (srcFile.isDirectory())
{
Collection 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方法,參數(shù)為要遍歷的目錄,一個null和一個false,第二個參數(shù)表示過濾器,表示過濾出特定后綴名的文件,類型為String [],第三個布爾參數(shù)表示是否遞歸訪問子目錄.
利用FileVisitor這個接口.實(shí)際中常用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
{
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失敗時的操作
}
}
在上面的例子中只是實(shí)現(xiàn)了visitFile,因?yàn)橹皇菑?fù)制操作,首先判斷是否是源目錄的路徑,不是的話創(chuàng)建文件夾再復(fù)制文件.
這里說一下返回值FileVisitResult.FileVisitResult是一個枚舉類型,根據(jù)返回值判斷是否繼續(xù)遍歷.
FileVisitResult可取值:
首先是經(jīng)典的字節(jié)流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方法的區(qū)別,FileInputStream有三個read方法:
input.read();
input.read(b);
input.read(b,off,len);
逐個字節(jié)進(jìn)行讀取,返回int,寫入時直接使用write(n);
int n = input.read();
output.write(n);
這個可以說是三個read中最慢的....作者試了一個2G左右的文件,用了大概10分鐘才復(fù)制160M......
參數(shù)是一個byte [],將字節(jié)緩沖到其中,返回?cái)?shù)組的字節(jié)個數(shù),這個比read()快很多.
byte [] b = new byte[1024];
while(input.read(b) != -1)
output.write(b);
這個方法其實(shí)和read(b)差不多,read(b)相當(dāng)于省略了參數(shù)的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);
}
這兩個都是調(diào)用一樣的readBytes():
private native int readBytes(byte b[], int off, int len) throws IOException;
至于效率...可以看看結(jié)果(作者用的是10G內(nèi)的小文件):
可以看到,沒有哪個一定比另外一個更快(不過最后一個誤差有點(diǎn)太大了?7G不夠的文件.).
采用哪一個建議自己去測試,畢竟這存在很多誤差,比如文件,java版本,機(jī)器本身等等,僅供參考.
緩沖字節(jié)流BufferedInputStream+BufferedOutputStream,相比起FileInputStream,BufferedInputStream讀取時會先從緩沖區(qū)讀取數(shù)據(jù),緩沖區(qū)無可讀數(shù)據(jù)再從文件讀取,所以會比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(實(shí)際上還有,還有readN,與read(),read()肯定最慢,readN作者很少用,所以就沒列出來了)
read(b);
read(b,off,len);
readAllBytes();
這個其實(shí)和FileInputStream的那個沒啥區(qū)別,把一個字節(jié)數(shù)組仍進(jìn)去就好了.
這個....也和FileInputStream那個沒啥區(qū)別,不說了
這個一次可以讀取所有的字節(jié).不過用這個雖然省事,可以直接
output.write(input.readAllBytes());
但是呢,有代價(jià)的:
會出現(xiàn)OutOfMemory錯誤,就是對于大文件還是老老實(shí)實(shí)分開吧,不要"一口搞定","多吃幾口".
看看效率:
readAllBytes對于大文件(作者這個是5G內(nèi)的文件)直接爆內(nèi)存....
readAllBytes()又爆了.....這個才2G不到的文件...readAllBytes()看來不是很給力啊....不過對于小文件效率還可以接受.
字符流讀寫FileReader+FileWriter,相比起字節(jié)流的read,基本上把byte[]換成char[]即可,因?yàn)槭侵饌€字符讀取,而字節(jié)流是逐個字節(jié)讀取因此采用byte[].
注意這個不能用來讀取圖片,音樂等文件,不然復(fù)制出來的文件打不開.
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();
}
緩沖字符流讀寫B(tài)ufferedReader+BufferedWriter,BufferedReader相比起FileReader有一個readLine()方法,可以每行讀入,會比FileReader快.對應(yīng)的BufferedWriter提供了write(String)方法,當(dāng)然也有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();
}
通過FileChannel復(fù)制,首先通過FileInputStream與FileOutputStream打開流,再用getChannel()方法.最后使用transferTo()或transferFrom()進(jìn)行復(fù)制,一條語句即可,十分方便,而且效率很高.
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();
}
在利用了FileInputStream與FileOutputStream打開了FileChannel的基礎(chǔ)上,配合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的意思是"翻轉(zhuǎn)",
buffer.flip();
把Buffer從寫模式變?yōu)樽x模式,接著write(buffer),再把buffer清空.
看看這兩種方法效率:
另外作者發(fā)現(xiàn)transferTo的"上限"為2G,就是對于大于2G的單個文件最多最能復(fù)制2個G.
所以...對于大文件沒有可比性了.
這是工具類,沒啥好說的,參數(shù)是兩個File,分別表示源與目標(biāo).
private static void copyByCommonsIO(File srcFile,File desFile) throws IOException
{
FileUtils.copyFile(srcFile, desFile);
}
這是官方提供的Files工具類,前兩個參數(shù)為Path,分別表示源與目標(biāo),可以設(shè)置第三個參數(shù)(或者省略),表示選項(xiàng).例如可以設(shè)置
StandardCopyOption.REPLACE_EXISTING
private static void copyByFiles(File srcFile,File desFile) throws IOException
{
Files.copy(srcFile.toPath(), desFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
注意Files.copy會保持文件的隱藏屬性,原來是隱藏的文件復(fù)制后也是隱藏的.以上7種則不會.
主JFrame采用了網(wǎng)格布局
setLayout(new GridLayout(3,1,5,3));
三行一列,因?yàn)橹灰齻€按鈕,選擇源文件(夾),選擇目標(biāo)文件夾,選擇遍歷方式.
選擇遍歷方式/復(fù)制方式的JFrame同樣適用了網(wǎng)格布局:
showTraverseMethod.setLayout(new GridLayout(5,1,3,3));
showCopyMethod.setLayout(new GridLayout(4,2,5,5));
setBounds(
(int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - 200,
(int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - 200,
400, 400);
高400,寬400,利用ToolKit.getDefaultToolKit().getScreenSize()獲取屏幕的高度和寬度實(shí)現(xiàn)居中.
由于在主JFrame中只有三個按鈕,選擇完遍歷方式后需要更新這個組件,作者的做法是先刪除這個組件在添加組件:
traverseMethodButton.setVisible(false);
remove(traverseMethodButton);
add(copyMethodButton);
copyMethodButton.setVisible(true);
設(shè)置它不可見再刪除,再添加另一組件,再設(shè)置可見.
進(jìn)度條這個東西把作者搞得很慘啊......其實(shí)就是新建一個線程就可以了.
核心代碼為:
new Thread(
() ->
{
int percent;
while ((percent = getCopyPercent()) < 100)
{
try
{
Thread.sleep(100);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
copyProgressBar.setValue(percent);
}
}
).start();
作者的JProgressBar是直接添加在一個JFrame中的,不用什么太復(fù)雜的布局.
獲取百分比后調(diào)用setValue(),一定要新建一個線程操作,不然不能正常顯示進(jìn)度條.
另外復(fù)制的操作建議使用SwingWorker.
SwingWorker 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("未知錯誤復(fù)制失敗");
}
finish(start);
return null;
}
};
copyTask.execute();
說了那么多來點(diǎn)實(shí)際的.
(以下所有的測試都是刪除復(fù)制的文件后再進(jìn)行新一次的復(fù)制.)
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 |
文件的話其實(shí)縱向比較即可,因?yàn)榛静挥迷趺幢闅v,橫向比較可以勉強(qiáng)看作求平均值.
對于非文本文件,FileReader/Writer和BufferedReader/Writer沒有太大的參考意義,比如復(fù)制視頻文件是打不開的,而且復(fù)制出來的文件會變大.對于單文件Files.copy的性能非常好,java的nio果然厲害.
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的文件是文本文件.
現(xiàn)在可以看看FileChannel的這一行,明顯所花的時間要比其他要少,為什么呢?
因?yàn)槲募笥?G.FileChannel的trasferTo方法只能寫入最多2G的文件,所以對于大于2G的文件復(fù)制出來只有2G,因此FileChannel的這一行沒有太大可比性.對于文本文件,BufferedReader/Writer的復(fù)制速度是最快的了,其次是FileInput/OutputStream.對于單個大文件,apache的FileUtils與NIO的Files.copy的速度比FileInputStream慢啊...
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()進(jìn)行復(fù)制,工具類FileUtils的復(fù)制方法表現(xiàn)還是不錯的,但相比起java標(biāo)準(zhǔn)的Files.copy效率都差了.
對于FileChannel與配合緩沖使用的FileChannel,1G的話好像不相上下.
遍歷方式的話...可以看到plexus的遍歷方法表現(xiàn)差距很大,而apache的listFiles或者java nio的walkFileTree比較穩(wěn)定且速度還可以,推薦使用這兩種方式遍歷目錄.
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使用,不過從數(shù)據(jù)上看效果比BufferedInput/OutputStream要低.
再看看org.apache.commons.io.FileUtils與java.nio.file.Files的copy,差別不太,效果接近,但在1G的時候差距有點(diǎn)大.
遍歷方式的話,java nio的walkFileTrees最快.
當(dāng)然這些測試僅供參考,具體使用哪一個要看看具體環(huán)境,另外這種方式把遍歷與復(fù)制分開,apache的FileUtils有方法可以直接復(fù)制目錄的,因此,使用哪個更合適還需要個人具體測試.
作者比較偷懶全部仍在一個文件了.七百行.
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("復(fù)制方式");
private JButton traverseMethodButton = new JButton("請選擇遍歷方式");
private JButton copyMethodButton = new JButton("請選擇復(fù)制方式");
private JButton copyButton = new JButton("開始復(fù)制");
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("選擇目標(biāo)文件夾");
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("復(fù)制方式");
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,"請先選擇源文件(夾)與目標(biāo)文件夾!");
}
);
setLayout(new GridLayout(3,1,5,3));
setTitle("復(fù)制文件");
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 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 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("復(fù)制失敗,找不到復(fù)制方法.");
}
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 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("未知錯誤復(fù)制失敗");
}
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("復(fù)制進(jìn)度");
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("復(fù)制中");
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("復(fù)制中"+text[index++]);
else
index = 0;
}
}
).start();
}
}
}
private static void finish(long start)
{
long end = System.currentTimeMillis();
showProgressBar.dispose();
showMessage("復(fù)制成功,用時:" + (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;
}
}