最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java多種文件復(fù)制方式以及效率對(duì)比分析

 更新時(shí)間:2025年11月24日 14:22:47   作者:氷泠  
本文總結(jié)了Java復(fù)制文件的多種方式,包括傳統(tǒng)的字節(jié)流、字符流、NIO系列、第三方包中的FileUtils等,并提供了不同方式的效率比較,同時(shí),還介紹了遍歷文件和文件夾的方法,并使用Swing進(jìn)行了簡(jiǎn)單的圖形界面展示,通過(guò)實(shí)際測(cè)試,對(duì)比了不同方法的性能,提供了參考

1 背景

Java復(fù)制文件的方式其實(shí)有很多種,可以分為:

  • 傳統(tǒng)的字節(jié)流讀寫(xiě)復(fù)制FileInputStream、FileOutputStream、BufferedInputStreamBufferedOutputStream
  • 傳統(tǒng)的字符流讀寫(xiě)復(fù)制FileReader、FileWriter、BufferWriter、BufferedWriter、BufferedReader
  • NIO系列的FileChannel
  • FileChannel+緩沖
  • java.nio.Files.copy()
  • 第三方包中的FileUtils.copy方法,比如org.apache.commons.io.FileUtils、org.codehaus.plexus.util.FileUtils等等

所以呢,看看各種方法效率怎么樣,主要衡量的標(biāo)準(zhǔn)就是時(shí)間,另外的一些標(biāo)準(zhǔn)包括大文件的復(fù)制時(shí)的內(nèi)存溢出等問(wèn)題。

2 概述

由于很多時(shí)候復(fù)制文件都包括了文件夾下的所有子目錄及文件的復(fù)制,所以筆者采用的遍歷+復(fù)制方法去復(fù)制文件,就是把整個(gè)復(fù)制過(guò)程分為先遍歷,遍歷的過(guò)程中遇到文件夾就創(chuàng)建,遇到文件就調(diào)用不同的復(fù)制方法。

遍歷的5種方法:

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

復(fù)制的8種方法:

  • FileInputStream+FileOutputStream
  • BufferedInputStream+BufferedOutputStream
  • FileReader+FileWriter
  • BufferedReader+BufferedWriter
  • FileChannel
  • FileChannel+buffer
  • org.apache.commons.io.FileUtils.copyFile()
  • java.nio.file.Files.copy()

另外筆者不太想看控制臺(tái),所以配合了一點(diǎn)swing使用。

3 遍歷

3.1listFiles()

 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);
	}
}

通過(guò)srcFilelistFiles()獲取所有的子文件與子文件夾,然后判斷是否是目錄:

  • 如果是目錄,首先判斷有沒(méi)有這個(gè)文件(有時(shí)候本來(lái)是文件夾但是卻存在同名的文件,就先刪除),再創(chuàng)建文件夾,然后遞歸執(zhí)行函數(shù)
  • 如果不是目錄,直接把兩個(gè)File作為參數(shù)進(jìn)行文件復(fù)制,里面用什么方法后面會(huì)設(shè)置

3.2list()

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()類(lèi)似,不過(guò)是String[],也是先判斷目錄,創(chuàng)建目錄,不是目錄直接復(fù)制。

3.3org.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);
	}
}

這是用了別人的工具類(lèi)進(jìn)行遍歷.

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

返回的結(jié)果是java.util.List

3.4Commons.io

private static void traverseByCommonsIO(File srcFile, File desFile) throws IOException
{
	if (srcFile.isDirectory())
	{
		Collection<File> files = org.apache.commons.io.FileUtils.listFiles(srcFile,null,false);
		for (File file : files)
		{
			File desFileOrDir = new File(desFile.getAbsolutePath() + File.separator + file.getName());
			if(file.isDirectory())
			{
				if(desFileOrDir.exists())
					desFileOrDir.delete();
				desFileOrDir.mkdirs();
			}
			traverseByCommonsIO(file, desFileOrDir);
        }
	}
	else 
	{
		copyFile(srcFile, desFile);
	}
}

使用org.apache.commons.io.FileUtilslistFiles方法,參數(shù)為要遍歷的目錄,一個(gè)null和一個(gè)false,第二個(gè)參數(shù)表示過(guò)濾器,表示過(guò)濾出特定后綴名的文件,類(lèi)型為String [],第三個(gè)布爾參數(shù)表示是否遞歸訪問(wèn)子目錄。

3.5walkFileTree

利用FileVisitor這個(gè)接口,實(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接口定義了四個(gè)方法,分別為:

public interface FileVisitor<T>
{
	FileVisitResult preVisitDirectory(T dir,BasicFileAttributes attrs)
	{
		//訪問(wèn)dir前的操作,dir類(lèi)型一般為java.nio.Path
	}
	
	FileVisitResult postVisitDirectory(T dir,BasicFileAttributes attrs)
	{
		//訪問(wèn)dir后的操作
	}
	
	FileVisitResult visitFile(T file,BasicFileAttributes attrs)
	{
		//訪問(wèn)file時(shí)的操作
	}
	
	FileVisitResult visitFileFailed(T file,BasicFileAttributes attrs)
	{
		//訪問(wèn)file失敗時(shí)的操作
	}
}

在上面的例子中只是實(shí)現(xiàn)了visitFile,因?yàn)橹皇菑?fù)制操作,首先判斷是否是源目錄的路徑,不是的話創(chuàng)建文件夾再?gòu)?fù)制文件。

這里說(shuō)一下返回值FileVisitResult,FileVisitResult是一個(gè)枚舉類(lèi)型,根據(jù)返回值判斷是否繼續(xù)遍歷,可取值如下:

  • CONTINUE:繼續(xù)
  • TERMINNATE:結(jié)束
  • SKIP_SIBLINGS:繼續(xù),跳過(guò)同一目錄的節(jié)點(diǎn)
  • SKIP_SUBTREE:繼續(xù),跳過(guò)子目錄,但會(huì)訪問(wèn)子文件

4 復(fù)制

4.1FileInputStream+FileOutputStream

首先是經(jīng)典的字節(jié)流FileInputStream+FileOutputStream,這個(gè)比較簡(jiǎn)單,使用FileInputStream讀取后使用FileOutputStream寫(xiě)入,不過(guò)效率嘛…一般般。

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();
}

注意一下三個(gè)read()的區(qū)別:

  • input.read()
  • input.read(b)
  • input.read(b,off,len)

4.1.1read()

逐個(gè)字節(jié)進(jìn)行讀取,返回int,寫(xiě)入時(shí)直接使用write(n)

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

這個(gè)可以說(shuō)是三個(gè)read中最慢的…測(cè)試了一個(gè)2G左右的文件,用了大概10分鐘才復(fù)制160M。

4.1.2read(b)

參數(shù)是一個(gè)byte [],將字節(jié)緩沖到其中,返回?cái)?shù)組的字節(jié)個(gè)數(shù),這個(gè)比read()快很多:

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

4.1.3read(b,off,len)

這個(gè)方法其實(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);
}

這兩個(gè)都是調(diào)用一樣的readBytes()

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

至于效率…可以看看結(jié)果,使用10G內(nèi)的小文件進(jìn)行測(cè)試:

可以看到,沒(méi)有哪個(gè)一定比另外一個(gè)更快(不過(guò)最后一個(gè)誤差有點(diǎn)太大了?7G不夠的文件)。

采用哪一個(gè)建議自己去測(cè)試,畢竟這存在很多誤差,比如文件,Java版本,機(jī)器本身等等,僅供參考。

4.2BufferedInputStream+BufferedOutputStream

緩沖字節(jié)流BufferedInputStream+BufferedOutputStream,相比起FileInputStream,BufferedInputStream讀取時(shí)會(huì)先從緩沖區(qū)讀取數(shù)據(jù),緩沖區(qū)無(wú)可讀數(shù)據(jù)再?gòu)奈募x取,所以會(huì)比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();
}

這里也說(shuō)一下BufferedInputStream的三個(gè)read

  • read(b)
  • read(b,off,len)
  • readAllBytes()

4.2.1read(b)

這個(gè)其實(shí)和FileInputStream的那個(gè)沒(méi)啥區(qū)別,把一個(gè)字節(jié)數(shù)組仍進(jìn)去就好了。

4.2.2read(b,off,len)

這個(gè)…也和FileInputStream那個(gè)沒(méi)啥區(qū)別,不說(shuō)了。

4.2.3readAllBytes()

這個(gè)一次可以讀取所有的字節(jié)。不過(guò)用這個(gè)雖然省事,可以直接

output.write(input.readAllBytes());

但是呢,有代價(jià)的:

會(huì)出現(xiàn)OutOfMemory錯(cuò)誤,就是對(duì)于大文件還是老老實(shí)實(shí)分開(kāi)吧。

看看效率:

readAllBytes對(duì)于大文件(測(cè)試的是5G內(nèi)的文件)直接爆內(nèi)存…

readAllBytes()又爆了…這個(gè)才2G不到的文件…readAllBytes()看來(lái)不是很給力啊…不過(guò)對(duì)于小文件效率還可以接受。

4.3FileReader+FileWriter

字符流讀寫(xiě)FileReader+FileWriter,相比起字節(jié)流的read,基本上把byte[]換成char[]即可,因?yàn)槭侵饌€(gè)字符讀取,而字節(jié)流是逐個(gè)字節(jié)讀取因此采用byte[]。

注意這個(gè)不能用來(lái)讀取圖片、音樂(lè)等文件,不然復(fù)制出來(lái)的文件打不開(kāi)。

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.4BufferedReader+BufferedWriter

緩沖字符流讀寫(xiě)BufferedReader+BufferedWriter,BufferedReader相比起FileReader有一個(gè)readLine()方法,可以每行讀入,會(huì)比FileReader快。對(duì)應(yīng)的BufferedWriter提供了write(String)方法,當(dāng)然也有write(String s,int off,int len),同樣這個(gè)不能用來(lái)讀取圖片等。

private static void copyByBufferReader(File srcFile,File desFile) throws IOException
{
	BufferedReader reader = new BufferedReader(new FileReader(srcFile));
	BufferedWriter writer = new BufferedWriter(new FileWriter(desFile));

	char [] c = new char[1024];
	while(reader.read(c) != -1)
	{
		addCopySize();
		writer.write(c);
	}
	reader.close();
	writer.close();
}

4.5FileChannel

通過(guò)FileChannel復(fù)制,首先通過(guò)FileInputStreamFileOutputStream打開(kāi)流,再用getChannel()方法。最后使用transferTo()transferFrom()進(jìn)行復(fù)制,一條語(yǔ)句即可,十分方便,而且效率很高。

private static void copyByFileChannel(File srcFile,File desFile) throws IOException
{
	FileChannel srcChannel = new FileInputStream(srcFile).getChannel();
	FileChannel desChannel = new FileOutputStream(desFile).getChannel();
	srcChannel.transferTo(0,srcChannel.size(),desChannel);
	srcChannel.close();
	desChannel.close();
}

4.6FileChannel+ByteBuffer

在利用了FileInputStreamFileOutputStream打開(kāi)了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從寫(xiě)模式變?yōu)樽x模式,接著write(buffer),再清空。

看看這兩種方法效率:

另外測(cè)試的時(shí)候發(fā)現(xiàn)transferTo的"上限"為2G,就是對(duì)于大于2G的單個(gè)文件最多最能復(fù)制2個(gè)G。

所以…對(duì)于大文件沒(méi)有可比性了。

4.7FileUtils.copyFile()

這是工具類(lèi),沒(méi)啥好說(shuō)的,參數(shù)是兩個(gè)File,分別表示源與目標(biāo)。

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

4.8Files.copy()

這是官方提供的Files工具類(lèi),前兩個(gè)參數(shù)為Path,分別表示源與目標(biāo),可以設(shè)置第三個(gè)參數(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會(huì)保持文件的隱藏屬性,原來(lái)是隱藏的文件復(fù)制后也是隱藏的,以上7種則不會(huì)。

5 其他

5.1 swing布局

5.1.1 網(wǎng)格布局

JFrame采用了網(wǎng)格布局

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

三行一列,因?yàn)橹灰齻€(gè)按鈕,選擇源文件(夾)、選擇目標(biāo)文件夾、選擇遍歷方式。

選擇遍歷方式/復(fù)制方式的JFrame同樣使用了網(wǎng)格布局:

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

5.1.2 居中

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

高400,寬400,利用ToolKit.getDefaultToolKit().getScreenSize()獲取屏幕的高度和寬度實(shí)現(xiàn)居中。

5.1.3 組件的添加與刪除

由于在主JFrame中只有三個(gè)按鈕,選擇完遍歷方式后需要更新這個(gè)組件,這里的實(shí)現(xiàn)是先刪除這個(gè)組件在添加組件:

traverseMethodButton.setVisible(false);
remove(traverseMethodButton);
add(copyMethodButton);
copyMethodButton.setVisible(true);

設(shè)置它不可見(jiàn)再刪除,再添加另一組件,再設(shè)置可見(jiàn)。

5.2 進(jìn)度條

進(jìn)度條這個(gè)東西把搞了很久…其實(shí)就是新建一個(gè)線程就可以了。

核心代碼:

new Thread(
	() ->
	{
		int percent;
		while ((percent = getCopyPercent()) < 100)
		{
			try
			{
				Thread.sleep(100);
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
			copyProgressBar.setValue(percent);
		}
	}
).start();

JProgressBar是直接添加在一個(gè)JFrame中的,不用什么太復(fù)雜的布局。

獲取百分比后調(diào)用setValue(),一定要新建一個(gè)線程操作,不然不能正常顯示進(jìn)度條。

另外復(fù)制的操作建議使用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("未知錯(cuò)誤復(fù)制失敗");
		}
		finish(start);
		return null;
	}
};
copyTask.execute();

6 測(cè)試

下面進(jìn)行測(cè)試。測(cè)試的對(duì)象包括:

  • 1G文件
  • 10G文件
  • 1G目錄
  • 10G目錄

6.1 1G文件

文件的話其實(shí)縱向比較即可,因?yàn)榛静挥迷趺幢闅v,橫向比較可以勉強(qiáng)看作求平均值。

對(duì)于非文本文件,FileReader/WriterBufferedReader/Writer沒(méi)有太大的參考意義,比如復(fù)制視頻文件是打不開(kāi)的,而且復(fù)制出來(lái)的文件會(huì)變大。對(duì)于單文件Files.copy的性能非常好,NIO果然厲害。

6.2 10G文件

這個(gè)10G的文件是文本文件。

現(xiàn)在可以看看FileChannel的這一行,明顯所花的時(shí)間要比其他要少,為什么呢?

因?yàn)槲募笥?G,FileChanneltrasferTo方法只能寫(xiě)入最多2G的文件,所以對(duì)于大于2G的文件復(fù)制出來(lái)只有2G,因此FileChannel的這一行沒(méi)有太大可比性。對(duì)于文本文件,BufferedReader/Writer的復(fù)制速度是最快的了,其次是FileInput/OutputStream。對(duì)于單個(gè)大文件,FileUtilsFiles.copy的速度比FileInputStream慢。

6.3 1G目錄

對(duì)于目錄的話可以考慮放棄BufferedReaderFileReader了,除非全部是文本文件,否則推薦使用BufferedInput/OutputStreamFiles.copy()進(jìn)行復(fù)制,工具類(lèi)FileUtils的復(fù)制方法表現(xiàn)還是不錯(cuò)的,但相比起標(biāo)準(zhǔn)的Files.copy效率差了。

對(duì)于FileChannel與配合緩沖使用的FileChannel,1G的話好像不相上下。

遍歷方式的話…可以看到plexus的遍歷方法表現(xiàn)差距很大,而ApachelistFiles或者Java NIOwalkFileTree比較穩(wěn)定且速度還可以,推薦使用這兩種方式遍歷目錄。

6.4 10G目錄

FileReaderBufferedReader這兩行可以忽略了。對(duì)于小文件用FileChannel的話還是不錯(cuò)的,對(duì)于大文件一定要用FileChannel的話可以配合ByteBuffer使用,不過(guò)從數(shù)據(jù)上看效果比BufferedInput/OutputStream要低。

再看看org.apache.commons.io.FileUtilsjava.nio.file.Files的復(fù)制,差別不太,效果接近,但在1G的時(shí)候差距有點(diǎn)大。

遍歷方式的話,walkFileTrees最快。

當(dāng)然這些測(cè)試僅供參考,具體使用哪一個(gè)要看看具體環(huán)境,另外這種方式把遍歷與復(fù)制分開(kāi),ApacheFileUtils有方法可以直接復(fù)制目錄的,因此,使用哪個(gè)更合適還需要個(gè)人具體測(cè)試。

7 源碼

筆者比較偷懶代碼全部仍在一個(gè)文件了:

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("請(qǐng)選擇遍歷方式");
    private JButton copyMethodButton = new JButton("請(qǐng)選擇復(fù)制方式");
    private JButton copyButton = new JButton("開(kāi)始復(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,"請(qǐng)先選擇源文件(夾)與目標(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<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("復(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<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("未知錯(cuò)誤復(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ù)制成功,用時(shí):" + (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;
    }
}

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • idea直接修改新的git地址的方法(圖文)

    idea直接修改新的git地址的方法(圖文)

    這篇文章主要介紹了idea直接修改新的git地址的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Java異常架構(gòu)Exception(異常)詳解

    Java異常架構(gòu)Exception(異常)詳解

    這篇文章主要介紹了Java異常架構(gòu)Exception(異常),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • Java面向?qū)ο蟮姆庋b特征深度解析

    Java面向?qū)ο蟮姆庋b特征深度解析

    在面向?qū)ο蟪淌皆O(shè)計(jì)方法中,封裝(英語(yǔ):Encapsulation)是指一種將抽象性函式接口的實(shí)現(xiàn)細(xì)節(jié)部分包裝、隱藏起來(lái)的方法。封裝可以被認(rèn)為是一個(gè)保護(hù)屏障,防止該類(lèi)的代碼和數(shù)據(jù)被外部類(lèi)定義的代碼隨機(jī)訪問(wèn)
    2021-10-10
  • Nacos動(dòng)態(tài)配置管理機(jī)制方式

    Nacos動(dòng)態(tài)配置管理機(jī)制方式

    這篇文章主要介紹了Nacos動(dòng)態(tài)配置管理機(jī)制方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Java程序免安裝JDK的運(yùn)行方案

    Java程序免安裝JDK的運(yùn)行方案

    文章介紹了將JDK和Java程序一起打包,通過(guò)啟動(dòng)腳本直接調(diào)用內(nèi)置JDK,實(shí)現(xiàn)無(wú)需安裝JDK即可一鍵運(yùn)行Java程序,適合綠色部署、低技術(shù)門(mén)檻和快速交付場(chǎng)景,需要的朋友可以參考下
    2025-10-10
  • 自定義指定路由上的Gateway過(guò)濾器工廠詳解

    自定義指定路由上的Gateway過(guò)濾器工廠詳解

    這篇文章主要介紹了自定義指定路由上的Gateway過(guò)濾器工廠詳解,gateway是Spring?Cloud中的一個(gè)網(wǎng)關(guān)服務(wù),gateway可以使用服務(wù)注冊(cè)中心進(jìn)行服務(wù)發(fā)現(xiàn)和負(fù)載均衡,同時(shí)還可以配置斷言來(lái)判斷請(qǐng)求是否符合路由規(guī)則,需要的朋友可以參考下
    2023-09-09
  • 關(guān)于jackson序列化和feign返回值的問(wèn)題

    關(guān)于jackson序列化和feign返回值的問(wèn)題

    這篇文章主要介紹了關(guān)于jackson序列化和feign返回值的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Maven 多profile及指定編譯問(wèn)題的解決

    Maven 多profile及指定編譯問(wèn)題的解決

    這篇文章主要介紹了Maven 多profile及指定編譯問(wèn)題的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 詳解Spring注入集合(數(shù)組、List、Map、Set)類(lèi)型屬性

    詳解Spring注入集合(數(shù)組、List、Map、Set)類(lèi)型屬性

    這篇文章主要介紹了詳解Spring注入集合(數(shù)組、List、Map、Set)類(lèi)型屬性,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • SpringBoot+MybatisPlus實(shí)現(xiàn)sharding-jdbc分庫(kù)分表的示例代碼

    SpringBoot+MybatisPlus實(shí)現(xiàn)sharding-jdbc分庫(kù)分表的示例代碼

    本文主要介紹了SpringBoot+MybatisPlus實(shí)現(xiàn)sharding-jdbc分庫(kù)分表的示例代碼,以分庫(kù),分表,分庫(kù)分表三種方式來(lái)實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03

最新評(píng)論

潞西市| 阳信县| 丹阳市| 长泰县| 定安县| 读书| 石门县| 甘南县| 南丰县| 郴州市| 镇江市| 中西区| 台南县| 酉阳| 绥棱县| 武山县| 宜兰县| 堆龙德庆县| 二手房| 大厂| 雷州市| 东安县| 莱西市| 肇源县| 突泉县| 乐至县| 郓城县| 图木舒克市| 宁晋县| 靖边县| 枣阳市| 安塞县| 湘潭县| 太仆寺旗| 轮台县| 南雄市| 清流县| 濮阳市| 张家口市| 兖州市| 米林县|