java複製文件的方式其實有不少種,能夠分爲java
因此呢,看看各類方法效率怎麼樣,主要衡量的標準就是時間,另外的一些標準包括大文件的複製時的內存溢出等問題.apache
因爲不少時候複製文件都包括了文件夾下的全部子目錄及文件的複製,因此做者採用的遍歷+複製方法去複製文件.就是把整個複製過程分爲先遍歷,遍歷的過程當中遇到文件夾就建立,遇到文件就調用不一樣的複製方法.
遍歷的5種方法:數組
複製的8種方法:maven
另外做者不太想看控制檯.....因此配合了一點swing使用.ide
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()獲取全部的子文件與子文件夾,而後判斷是不是目錄
若是是目錄,首先判斷有沒有這個文件(有時候原本是文件夾可是卻存在同名的文件,就先刪除),再建立文件夾,而後遞歸執行函數.
若是不是目錄,直接把兩個File做爲參數進行文件複製,裏面用什麼方法後面會設置.工具
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[],也是先判斷目錄,建立目錄,不是目錄直接複製佈局
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); } }
這是用了別人的工具類進行遍歷.post
org.codehaus.plexus.util.FileUtils.getFiles(srcFile,null,null);
返回的結果的java.util.List性能
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 [],第三個布爾參數表示是否遞歸訪問子目錄.
利用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是一個枚舉類型,根據返回值判斷是否繼續遍歷.
FileVisitResult可取值:
首先是經典的字節流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方法的區別,FileInputStream有三個read方法:
input.read(); input.read(b); input.read(b,off,len);
逐個字節進行讀取,返回int,寫入時直接使用write(n);
int n = input.read(); output.write(n);
這個能夠說是三個read中最慢的....做者試了一個2G左右的文件,用了大概10分鐘才複製160M......
參數是一個byte [],將字節緩衝到其中,返回數組的字節個數,這個比read()快不少.
byte [] b = new byte[1024]; while(input.read(b) != -1) output.write(b);
這個方法其實和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版本,機器自己等等,僅供參考.
緩衝字節流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(實際上還有,還有readN,與read(),read()確定最慢,readN做者不多用,因此就沒列出來了)
read(b); read(b,off,len); readAllBytes();
這個其實和FileInputStream的那個沒啥區別,把一個字節數組仍進去就行了.
這個....也和FileInputStream那個沒啥區別,不說了
這個一次能夠讀取全部的字節.不過用這個雖然省事,能夠直接
output.write(input.readAllBytes());
可是呢,有代價的:
會出現OutOfMemory錯誤,就是對於大文件仍是老老實實分開吧,不要"一口搞定","多吃幾口".
看看效率:
readAllBytes對於大文件(做者這個是5G內的文件)直接爆內存....
readAllBytes()又爆了.....這個才2G不到的文件...readAllBytes()看來不是很給力啊....不過對於小文件效率還能夠接受.
字符流讀寫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(); }
緩衝字符流讀寫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(); }
經過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(); }
在利用了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),再把buffer清空.
看看這兩種方法效率:
另外做者發現transferTo的"上限"爲2G,就是對於大於2G的單個文件最多最能複製2個G.
因此...對於大文件沒有可比性了.
這是工具類,沒啥好說的,參數是兩個File,分別表示源與目標.
private static void copyByCommonsIO(File srcFile,File desFile) throws IOException { FileUtils.copyFile(srcFile, desFile); }
這是官方提供的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種則不會.
主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));
setBounds( (int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - 200, (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - 200, 400, 400);
高400,寬400,利用ToolKit.getDefaultToolKit().getScreenSize()獲取屏幕的高度和寬度實現居中.
因爲在主JFrame中只有三個按鈕,選擇完遍歷方式後須要更新這個組件,做者的作法是先刪除這個組件在添加組件:
traverseMethodButton.setVisible(false); remove(traverseMethodButton); add(copyMethodButton); copyMethodButton.setVisible(true);
設置它不可見再刪除,再添加另外一組件,再設置可見.
進度條這個東西把做者搞得很慘啊......其實就是新建一個線程就能夠了.
核心代碼爲:
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();
說了那麼多來點實際的.
(如下全部的測試都是刪除複製的文件後再進行新一次的複製.)
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果真厲害.
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慢啊...
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比較穩定且速度還能夠,推薦使用這兩種方式遍歷目錄.
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有方法能夠直接複製目錄的,所以,使用哪一個更合適還須要我的具體測試.
做者比較偷懶所有仍在一個文件了.七百行.
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; } }