1. Предпосылки
На самом деле существует множество способов копирования файлов в java, которые можно разделить на
- Традиционный поток байтов для чтения и записи копирует FileInputStream, FileOutputStream, BufferedInputStream, BufferedOutputStream
- Традиционное копирование потока символов для чтения и записи FileReader, FileWriter, BufferWriter, BufferedWriter, BufferedReader
- FileChannel для серии NIO
- 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.file.Files.walkFileTree в java nio
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()
К тому же автор не хочет смотреть на консоль...поэтому пользуюсь с небольшим замахом.
3.баночный пакет
1.org.apache.commons 2.org.codehaus.plexus
4. Траверс
(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);
}
}
Получите все подфайлы и подпапки с помощью listFiles() из srcFile, а затем оцените, является ли это каталогом. Если это каталог, сначала определите, есть ли этот файл (иногда это папка, но есть файл с таким же именем, сначала удалите его), затем создайте папку, а затем рекурсивно выполните функцию. Если это не каталог, напрямую используйте два файла в качестве параметров для копирования файла, а какой метод используется в нем, будет установлено позже.
(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)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. Список возвращаемых результатов
(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);
}
}
Используйте метод listFiles из org.apache.commons.io.FileUtils, параметр — это каталог, который нужно пройти, null и false, второй параметр представляет собой фильтр, что означает отфильтровывать файлы с определенным суффиксом, тип is String [], Третий логический параметр указывает, следует ли рекурсивно обращаться к подкаталогам.
(5)NIO--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 — это тип перечисления, в соответствии с возвращаемым значением определяющий, следует ли продолжать обход. FileVisitResult может принимать значения:
- ПРОДОЛЖИТЬ: продолжить
- ЗАВЕРШЕНИЕ: конец
- SKIP_SIBLINGS: продолжить, пропустить узлы в том же каталоге
- SKIP_SUBTREE: продолжить, пропустить подкаталоги, но получить доступ к подфайлам
5. Копировать
(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();
}
Давайте поговорим о разнице между тремя методами чтения.FileInputStream имеет три метода чтения:
input.read();
input.read(b);
input.read(b,off,len);
A.read()
Читать байт за байтом, возвращать int, использовать write(n) непосредственно при записи;
int n = input.read();
output.write(n);
Можно сказать, что это самое медленное из трех чтений.... Автор попробовал файл размером около 2G, и копирование 160M заняло около 10 минут...
B.read(b)
Параметр представляет собой byte[], буферизует в него байты и возвращает количество байтов в массиве, что намного быстрее, чем read().
byte [] b = new byte[1024];
while(input.read(b) != -1)
output.write(b);
C.read(b,off,len)
Этот метод на самом деле похож на 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, самой машины и т.д., только для ознакомления.(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 (на самом деле есть еще и readN, и read(), read() точно самая медленная, и автор readN редко ее использует, поэтому ее нет в списке)
read(b);
read(b,off,len);
readAllBytes();
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, по сравнению с чтением потока байтов, в основном заменяет 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)BufferedReader+BufferedWriter
Буферизованный поток символов для чтения и записи BufferedReader+BufferedWriter. По сравнению с FileReader, BufferedReader имеет метод readLine(), который может читать каждую строку, что быстрее, чем FileReader. Соответствующий BufferedWriter предоставляет метод записи (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();
}
(5)NIO--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();
}
(6)NIO--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();
}
флип означает «флип»,
buffer.flip();
Измените буфер из режима записи в режим чтения, затем запишите (буфер), а затем очистите буфер. Взгляните на эффективность этих двух методов:
Кроме того, автор обнаружил, что «верхний предел» transferTo составляет 2 ГБ, что означает, что один файл размером более 2 ГБ может быть скопирован максимум до 2 ГБ. Итак... нет сравнения для больших файлов.
(7)FileUtils.copyFile()
Это инструментальный класс, тут и говорить нечего, параметры два файла, которые представляют исходник и цель соответственно.
private static void copyByCommonsIO(File srcFile,File desFile) throws IOException
{
FileUtils.copyFile(srcFile, desFile);
}
(8)Files.copy()
Это официально предоставленный класс инструментов "Файлы". Первые два параметра – "Путь", которые представляют исходный и целевой объекты соответственно. Третий параметр (или опущенный) можно задать для представления параметров. Например, вы можете установить
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 типов не будут.
6. Другое
(1) качели
А. Макет сетки
Основной JFrame использует макет сетки
setLayout(new GridLayout(3,1,5,3));
Три строки и один столбец, потому что кнопок всего три, выберите исходный файл (папку), выберите целевую папку и выберите метод обхода. JFrames с параметрами обхода/копирования также применяют макет сетки:
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(), чтобы получить высоту и ширину экрана для достижения центрирования.
C. Добавление и удаление компонентов
Так как в основном JFrame всего три кнопки, этот компонент необходимо обновить после выбора метода обхода, авторский подход заключается в том, чтобы сначала удалить этот компонент, а затем добавить компонент:
traverseMethodButton.setVisible(false);
remove(traverseMethodButton);
add(copyMethodButton);
copyMethodButton.setVisible(true);
Сделайте его невидимым, удалите, добавьте другой компонент и сделайте видимым.
(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();
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, метод TransferTo FileChannel может записывать только файлы размером до 2G, поэтому для файлов размером более 2G копируется только 2G, поэтому эта строка FileChannel не очень сравнима.Для текстовых файлов скорость копирования BufferedReader/Writer Это самый быстрый, за ним следует FileInput/OutputStream.Для одного большого файла FileUtils apache и Files.copy NIO медленнее, чем 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 сильно различаются по производительности, в то время как listFiles от apache или walkFileTree от java nio относительно стабильны и имеют хорошую скорость Рекомендуется использовать эти два метода для обхода каталогов.
(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, разница не слишком большая, эффект близок, но разрыв немного велик, когда это 1G. С точки зрения обхода, WalkFileTrees java nio является самым быстрым.
Конечно, эти тесты предназначены только для справки. Какой из них использовать, зависит от конкретной среды. Кроме того, этот метод отделяет обход от копирования. У Apache FileUtils есть метод для прямого копирования каталогов. Поэтому, какой из них более подходит, требует личного тестирование.
8. Исходный код
Автор ленив и все равно в одном файле.Семьсот строк.
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;
}
}