java nio: walkFileTree实现文件夹复制移动删除

从java 1.7开始,java提供了java.noi.file.Files类用于更方便的实现文件/文件夹操作。 在Files中提供了丰富的静态方法用于文件操作,Files也提供了文件移动和复制操作(Files.move,Files.copy),但是对于不为的空文件夹,不能调用Files.move,Files.copy实现文件夹下所有文件的复制和移动。 根据Files.move,Files.copy的说明,如果要移动/复制包含子目录的文件夹,需要用Files.walkFileTree方法配合Files.move,Files.copy来实现。以下是实现代码:

NioFileUtil.java

package net.gdface.iadb;

import java.io.IOException;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;

/**
 * 使用于NIO实现文件夹的复制/移动,删除<br>
 * 需要java 1.7以上版本支持
 * @author guyadong
 *
 */
public class NioFileUtil {
    /**
     * 复制文件夹
     * @param source
     * @param target
     * @param options
     * @throws IOException
     * @see {@link #operateDir(boolean, Path, Path, CopyOption...)}
     */
    public static void copyDir(Path source, Path target, CopyOption... options) throws IOException{
        operateDir(false, source, target, options);
    }
    /**
     * 移动文件夹
     * @param source
     * @param target
     * @param options
     * @throws IOException
     * @see {@link #operateDir(boolean, Path, Path, CopyOption...)}
     */
    public static void moveDir(Path source, Path target, CopyOption... options) throws IOException{
        operateDir(true, source, target, options);
    }
    /**
     * 复制/移动文件夹
     * @param move 操作标记,为true时移动文件夹,否则为复制
     * @param source 要复制/移动的源文件夹
     * @param target 源文件夹要复制/移动到的目标文件夹
     * @param options 文件复制选项 
     * @throws IOException
     * @see Files#move(Path, Path, CopyOption...)
     * @see Files#copy(Path, Path, CopyOption...)
     * @see Files#walkFileTree(Path, java.nio.file.FileVisitor)
     */
    public static void operateDir(boolean move,Path source, Path target, CopyOption... options) throws IOException{
        if(null==source||!Files.isDirectory(source))
            throw new IllegalArgumentException("source must be directory");
        Path dest = target.resolve(source.getFileName());
        // 如果相同则返回
        if(Files.exists(dest)&&Files.isSameFile(source, dest))return;
        // 目标文件夹不能是源文件夹的子文件夹
        // isSub方法实现参见另一篇博客 http://blog.csdn.net/10km/article/details/54425614
        if(isSub(source,dest))
            throw new IllegalArgumentException("dest must not  be sub directory of source");
        boolean clear=true;     
        for(CopyOption option:options)
            if(StandardCopyOption.REPLACE_EXISTING==option){
                clear=false;
                break;
            }
        // 如果指定了REPLACE_EXISTING选项则不清除目标文件夹
        if(clear)
            deleteIfExists(dest);
        Files.walkFileTree(source, 
                new SimpleFileVisitor<Path>() {

                    @Override
                    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                        // 在目标文件夹中创建dir对应的子文件夹
                        Path subDir = 0==dir.compareTo(source)?dest:dest.resolve(dir.subpath(source.getNameCount(), dir.getNameCount()));
                        Files.createDirectories(subDir);
                        return FileVisitResult.CONTINUE;
                    }

                    @Override
                    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                        if(move)
                            Files.move(file, dest.resolve(file.subpath(source.getNameCount(), file.getNameCount())),options);
                        else
                            Files.copy(file, dest.resolve(file.subpath(source.getNameCount(), file.getNameCount())),options);
                        return FileVisitResult.CONTINUE;
                    }

                    @Override
                    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                        // 移动操作时删除源文件夹
                        if(move)
                            Files.delete(dir);
                        return super.postVisitDirectory(dir, exc);
                    }
                });
    }

    /**
     * 强制删除文件/文件夹(含不为空的文件夹)<br>
     * @param dir
     * @throws IOException
     * @see Files#deleteIfExists(Path)
     * @see Files#walkFileTree(Path, java.nio.file.FileVisitor)
     */
    public static void deleteIfExists(Path dir) throws IOException {
        try {
            Files.deleteIfExists(dir);
        } catch (DirectoryNotEmptyException e) {
            Files.walkFileTree(dir, 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 super.postVisitDirectory(dir, exc);
                }
            });
        }
    }

}

测试代码

    public void testCopy() throws IOException{
        Path start = Paths.get("d:\\tmp\\storeroot\\origin");
        Path target = Paths.get("E:\\tmp\\storeroot");
        NioFileUtil.copyDir(start, target);
    }

关于Files.walkFileTree方法的使用说明,参见这篇博客《walkFileTree、FileVisitor(遍历文件/目录)》,说得很详细了

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Hadoop实操

如何使用java代码通过JDBC连接Impala(附Github源码)

访问Impala的方式很多(如:impala-shell、ODBC、JDBC、Beeline),也可以通过Hue的来访问。关于Beeline方式连接Impala...

5195
来自专栏编程坑太多

springboot (八) 文件上传下载

943
来自专栏DT乱“码”

JSP+ajax+springMVC+MayBatis处理excel上传导入

jsp <div class="subtext1"> <div cla...

2079
来自专栏7号代码

Android应用界面开发——简单控件和Activity间传递数据

要想开发一个Android App,开发环境是必不可少的,所以学习之前应该先搭建环境,环境如下:

913
来自专栏IT笔记

SpringBoot开发案例之整合Dubbo提供者(一)

既然是开发案例,显然不会扯那么多老婆舌,有不清楚这两个东东的请自行百度。 ? 0.jpg 开发环境 JDK1.7、Maven、Eclipse、SpringBoo...

38013
来自专栏Hongten

android开发_mp3_player音乐播放器

============================================

653
来自专栏Hongten

java中interface的完整表述

我用一个工具:java Decompiler反编译工具查看jar包源码的时候,出现了以下代码:

681
来自专栏dalaoyang

SpringBoot文件上传下载

项目中经常会有上传和下载的需求,这篇文章简述一下springboot项目中实现简单的上传和下载。 新建springboot项目,前台页面使用的thymeleaf...

2939
来自专栏郭耀华‘s Blog

Android中级教程之----Log详解(Log.v,Log.d,Log.i,Log.w,Log.e)

在Android群里,经常会有人问我,Android Log是怎么用的,今天我就把从网上以及SDK里东拼西凑过来,让大家先一睹为快,希望对大家入门Androi...

34110
来自专栏FreeBuf

深入挖掘APP克隆实验

0×00前言 在上一篇文章《WebView域控不严格读取内部私有文件实验》中,对webview跨域访问进行了简单的实验,后续决定深入挖掘一下APP克隆,之前文章...

21510

扫码关注云+社区