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