在java中,無法直接對文件夾(目錄)進行復制、移動、刪除等操作。自己可以通過遞歸來實現。下面代碼給出遞歸實現的參考。
注意:對文件夾的相關操作簡單封裝成工具類,代碼注重遞歸的實現,不關注對傳入的路徑的判斷。比如說:複製操作中將父目錄複製到其子目錄下,這樣是非法的。但在對應的函數中並未進行判斷。
做過簡單的測試,未發現問題。若有問題,歡迎指出!
package com.directory;
import org.junit.Test;
import java.io.*;
/**
* @author passerbyYSQ
* @create 2019-11-15 20:24
*/
public class DirUtils {
private static int bufSize = 1024; //作爲緩衝區的字節數組的大小
private static byte[] buf = new byte[bufSize]; //作爲緩衝區的字節數組
public static int getBufSize() {
return bufSize;
}
//調用者可以自己修改作爲緩衝區的字節數組的大小
public static void setBufSize(int bufSize) {
DirUtils.bufSize = bufSize;
}
@Test
public void testCopy() {
try {
long costTime = copy("F:\\web前端設計與開發", "F:\\temp");
System.out.println("耗時:" + costTime + "ms"); //耗時:143ms
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
@Test
public void testDelete() {
long sta = System.currentTimeMillis();
delete("F:\\temp\\web前端設計與開發");
long end = System.currentTimeMillis();
System.out.println("耗時:" + (end - sta) + "ms"); //耗時:73ms
}
@Test
public void testMove() {
try {
move("F:\\web前端設計與開發", "F:\\temp");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
@Test
public void testFindFile() {
long sta = System.currentTimeMillis();
findFile("F:\\web前端設計與開發", "index.html");
long end = System.currentTimeMillis();
System.out.println("耗時:" + (end - sta) + "ms"); //耗時:45ms
}
/**
* 將指定目錄(或者文件)移動到指定的路徑(目錄)
* 注意:兩個路徑要求是絕對路徑
* @param srcPath 將要複製的目錄或者文件
* @param destPath 目標路徑(目錄)
* @return 耗費的時間(單位:ms)
*/
public static long move(String srcPath, String destPath) throws FileNotFoundException {
long sta = System.currentTimeMillis();
//先將源文件或者目錄複製到目標目錄
copy(srcPath, destPath); //如果srcPath填錯,直接將異常拋給調用者
//再刪除源文件或者源目錄
delete(srcPath);
long end = System.currentTimeMillis();
return (end - sta);
}
/**
* 將指定目錄(或者文件)複製到指定的路徑(目錄)
* 注意:兩個路徑要求是絕對路徑
* @param srcPath 將要複製的目錄或者文件
* @param destPath 目標路徑(目錄)
* @return 耗費的時間(單位:ms)
*/
public static long copy(String srcPath, String destPath) throws FileNotFoundException {
long sta = System.currentTimeMillis();
File file = new File(srcPath);
//如果源目錄或者源文件在硬盤上不存在,則向調用者拋出異常
if (!file.exists()) {
throw new FileNotFoundException();
}
//如果要複製的是個目錄,則在目標目錄下建一個同名目錄
if (file.isDirectory()) {
destPath += ("\\"+ file.getName());
}
copying(srcPath, destPath);
long end = System.currentTimeMillis();
return (end - sta);
}
private static void copying(String srcPath, String destPath) {
File file = new File(srcPath);
if (file.isFile()) {
//在複製之前,判斷當前文件的父目錄是否存在,不存在則創建後再複製
String parentPath = new File(destPath).getParent();
if (parentPath != null) {
File parentFile = new File(parentPath);
if (!parentFile.exists()) {
parentFile.mkdirs();
}
}
BufferedInputStream bis = null; //緩衝字節輸入流
BufferedOutputStream bos = null; //緩衝字節輸出流
try {
bis = new BufferedInputStream(new FileInputStream(file));
bos = new BufferedOutputStream(new FileOutputStream(destPath));
int len;
while ((len = bis.read(buf)) != -1) {
bos.write(buf, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bis != null) { //關閉了外層的包裝流,內層流也會自動關閉
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} else {
File[] fileList = file.listFiles();
for (File f : fileList) {
copying(f.getAbsolutePath(), destPath + "/" + f.getName());
}
}
}
/**
* 刪除指定的目錄或者文件
* 注意:如果是刪除目錄,該函數將該目錄內的所有子文件以及子目錄全刪除之後,
* 還會把這個空目錄給刪除掉
* @param filePath 指定的目錄或者文件的絕對路徑
*/
public static void delete(String filePath) {
File file = new File(filePath);
if (file.isFile()) {
file.delete();
} else {
File[] fileList = file.listFiles();
for (File f : fileList) {
delete(filePath + "/" + f.getName());
//如果當前是文件夾,在刪除該文件夾的所有文件及子文件夾之後(回溯時),將該空文件夾刪除
//如果在這裏加上代碼,那麼最終的外層目錄不會刪除
// if (f.isDirectory()) {
// f.delete();
// }
}
}
//回溯時,把空文件夾給刪掉
file.delete();
}
/**
* 在指定的目錄中搜索指定的文件,若找到,則將絕對路徑輸出到控制檯上
* 注意:不區分大小寫
* @param srcPath 搜索的目錄
* @param filename 目標文件的文件名
*/
public static void findFile(String srcPath , String filename) {
File file = new File(srcPath);
if (file.isFile()) {
if (file.getName().equalsIgnoreCase(filename)) {
System.out.println(srcPath); //file.getAbsolutePath()也可以
}
} else {
File[] fileList = file.listFiles();
for (File f : fileList) {
findFile(srcPath + "/" + f.getName(), filename);
}
}
}
}
關於NIO2,參見大佬博客:https://blog.csdn.net/oMaoYanEr/article/details/80961130
經過簡單測試,NIO2的效率比自己用遞歸實現快。
至於爲什麼在複製文件夾時耗時差不多,原因是因爲我在用walkFileTree()中使用了String的替換,String是不可變的字符序列,每次替換都要在字符串常量池中開闢一個新的空間來裝新的字符串,並將指針指向這塊的新的內存。這個過程相對來說是非常耗時的。尤其是當這棵目錄樹畢竟龐大時,遞歸的深度就會較大,那麼就會頻繁地進行上述的替換操作。
所以我猜測,我這種實現複製操作的方法,在對一棵比較大的目錄樹進行復制操作時,它的時間開銷會比遞歸實現要高!但我目前沒有想到其他實現方法。
package com.directory; import org.junit.Test; import java.io.File; import java.io.IOException; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; /** * @author passerbyYSQ * @create 2019-11-16 10:25 */ public class NIO2DirUtils { @Test public void testDelete() { long costTime = delete("F:\\temp\\web前端設計與開發"); System.out.println("耗時:" + costTime + "ms"); //耗時:35ms } @Test public void testCopy() { long costTime = copy("F:\\web前端設計與開發", "F:\\temp"); //long costTime = copy("F:\\web前端設計與開發\\實驗17:異步請求.zip", "F:\\temp"); System.out.println("耗時:" + costTime + "ms"); //耗時:138ms } @Test public void testFindFile() { long costTime = findFile("F:\\temp\\web前端設計與開發", "Index.html"); System.out.println("耗時:" + costTime + "ms"); //耗時:9ms } /** * 刪除指定目錄或者文件 * @param srcPath 可以是絕對路徑或者相對路徑 * @return 耗時(單位:ms) */ public static long delete(String srcPath) { long sta = System.currentTimeMillis(); Path start = Paths.get(srcPath); try { Files.walkFileTree(start, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { Files.delete(dir); return FileVisitResult.CONTINUE; } }); } catch (IOException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); return (end - sta); } /** * 源目錄或者源文件 -複製-> 目標目錄下 * @param srcPath 源目錄或者源文件的路徑(絕對路徑或者相對路徑) * @param destPath 目標目錄的路徑(絕對路徑或者相對路徑) */ public static long copy(String srcPath, String destPath) { long sta = System.currentTimeMillis(); Path start = Paths.get(srcPath); final String prefix = destPath + "/" + start.getFileName(); try { Files.walkFileTree(start, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { String newFilePos = file.toAbsolutePath().toString().replace(srcPath, prefix); Path newFile = Paths.get(newFilePos); if (!Files.exists(newFile.getParent())) { Files.createDirectories(newFile.getParent()); } Files.copy(file, newFile); return FileVisitResult.CONTINUE; } }); } catch (IOException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); return (end - sta); } /** * 在指定的目錄中搜索指定的文件,若找到,則將絕對路徑輸出到控制檯上 * 注意:不區分大小寫 * @param srcPath 搜索的目錄 * @param filename 目標文件的文件名 */ public static long findFile(String srcPath, String filename) { long sta = System.currentTimeMillis(); Path start = Paths.get(srcPath); try { Files.walkFileTree(start, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (file.getFileName().toString().equalsIgnoreCase(filename)) { System.out.println(file.toAbsolutePath()); //會自動調用toString()方法 //return FileVisitResult.TERMINATE;//這裏不建議寫這一行代碼,因爲目錄下可能不止有一個同名文件(在不同子目錄) } return FileVisitResult.CONTINUE; } }); } catch (IOException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); return (end - sta); } }