`

Java7中那些新特性 - 5 (文件系统篇)

阅读更多
  一个文件系统通常指的是一个或多个根目录,其下面包含一定的文件和子目录,并由此组成的目录结构。每一种文件系统都支持一种文件存储机制。这种机制有可能是一个设备,例如C盘或一个磁盘分区,或者是其它的某种组织文件系统空间的方式。Java7中的java.nio.file.FileStore就代表了一种文件存储机制。下面来举例说明如何获得FileStore以及其相关信息。

      
    public static final long kiloByte = 1024;

    public static void main(String[] args) {

        String format = "%-16s %-20s %-8s %-8s %12s %12s %12s\n";

        System.out.printf(format, "Name", "Filesystem", "Type", "Readonly", "Size(KB)", "Used(KB)", "Available(KB)");

        FileSystem fileSystem = FileSystems.getDefault();

        try {

            for (FileStore fileStore : fileSystem.getFileStores()) {

                long totalSpace = fileStore.getTotalSpace() / kiloByte;
                long usedSpace = (fileStore.getTotalSpace() - fileStore.getUnallocatedSpace()) / kiloByte;
                long usableSpace = fileStore.getUsableSpace() / kiloByte;

                String name = fileStore.name();
                String type = fileStore.type();
                boolean readOnly = fileStore.isReadOnly();

                NumberFormat numberFormat = NumberFormat.getInstance();

                System.out.printf(format, name, fileStore, type, readOnly, numberFormat.format(totalSpace),
                        numberFormat.format(usedSpace), numberFormat.format(usableSpace));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

  上面这个例子中,我们在控制台上打印出来了各个磁盘的名称,类型以及磁盘空间使用情况。与此同时FileSystem类提供了getRootDirectories方法来获得磁盘的各个跟目录(Linux中通常只有一个根目录/,但Windows中通常有多个根目录C: D: E:)。参照下面的例子来看看如何打印出磁盘的各个根目录。

    public static void main(String[] args) {

        FileSystem fileSystem = FileSystems.getDefault();
        FileSystemProvider provider = fileSystem.provider();

        System.out.println("Provider: " + provider.toString());
        System.out.println("Open: " + fileSystem.isOpen());
        System.out.println("Read Only: " + fileSystem.isReadOnly());

        Iterable<Path> rootDirectories = fileSystem.getRootDirectories();

        System.out.println();
        System.out.println("Root Directories");

        for (Path path : rootDirectories) {
            System.out.println(path);
        }
    }


  当我们和一个目录文件系统打交道的时候,很多情况下都要遍历整个文件夹以及其子文件夹。Java7中对此也提供了新的API - java.nio.file.SimpleFileVisitor。话不多说直接看例子:
    public static void main(String[] args) {

        try {

            Path path = Paths.get("D:/Home/sample");
            ListFiles listFiles = new ListFiles();
            Files.walkFileTree(path, listFiles);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }


class ListFiles extends SimpleFileVisitor<Path> {

    private final int indentionAmount = 3;
    private int indentionLevel;

    public ListFiles() {
        indentionLevel = 0;
    }

    private void indent() {
        for (int i = 0; i < indentionLevel; i++) {
            System.out.print(' ');
        }
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        indent();
        System.out.println("Visiting file:" + file.getFileName());
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
        indentionLevel = indentionLevel - indentionAmount;
        indent();
        System.out.println("Finished with the directory: " + dir.getFileName());
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        indent();
        System.out.println("About to traverse the directory: " + dir.getFileName());
        indentionLevel = indentionLevel + indentionAmount;
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
        System.out.println("A file traversal error ocurred");
        return super.visitFileFailed(file, exc);
    }


  Files的walkFileTree方法以我们给定的一个目录作为根目录,对每一级文件夹都做了遍历。SimpleFileVisitor接口的每一个方法通过其名称就能知道其具体用途。本人理解即是对访问目录前,访问文件以及访问目录后的回调函数。每个方法都返回一个FileVisitResult,上层API由这个返回值来决定是否继续访问后面的目录以及文件。

  我们之前的博客提到过,如果想删除一个文件夹,文件夹必须是空的,那有没有办法能做到删除一个包含了很多目录和文件的文件夹呢?此时,我们就可以通过SimpleFileVisitor来实现。

    public static void main(String[] args) {

        try {
            Files.walkFileTree(Paths.get("D:/Home/sample/subtest"), new DeleteDirectory());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



class DeleteDirectory extends SimpleFileVisitor<Path> {

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        System.out.println("Deleting " + file.getFileName());
        Files.delete(file);
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exception) throws IOException {
        if (exception == null) {
            System.out.println("Deleting " + dir.getFileName());
            Files.delete(dir);
            return FileVisitResult.CONTINUE;
        } else {
            throw exception;
        }
    }

}


  同样的方法,我们也可以复制一个文件夹下所有的内容到一个新文件夹下。
    public static void main(String[] args) {
        try {
            Path source = Paths.get("D:/Home/sample");
            Path target = Paths.get("D:/Home/backup");
            Files.walkFileTree(source, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new CopyDirectory(
                    source, target));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }



class CopyDirectory extends SimpleFileVisitor<Path> {

    private Path source;
    private Path target;

    public CopyDirectory(Path source, Path target) {
        this.source = source;
        this.target = target;
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        System.out.println("Copying " + source.relativize(file));
        Files.copy(file, target.resolve(source.relativize(file)));
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        Path targetDirectory = target.resolve(source.relativize(dir));
        try {
            System.out.println("Copying " + source.relativize(dir));
            Files.copy(dir, targetDirectory);
        } catch (FileAlreadyExistsException e) {
            if (!Files.isDirectory(targetDirectory)) {
                throw e;
            }
        }
        return FileVisitResult.CONTINUE;
    }

}


  最后我们来看看如何监控一个目录下的变化,并对不同变化作出一些处理。

    public static void main(String[] args) {

        try {

            FileSystem fileSystem = FileSystems.getDefault();
            WatchService watchService = fileSystem.newWatchService();

            Path dir = Paths.get("D:/home/sample");

            WatchEvent.Kind<?>[] events = {
                StandardWatchEventKinds.ENTRY_CREATE, //
                StandardWatchEventKinds.ENTRY_MODIFY, //
                StandardWatchEventKinds.ENTRY_DELETE
            };

            dir.register(watchService, events);

            while (true) {
                System.out.println("Waiting for a watch event");
                WatchKey watchKey = watchService.take();
                System.out.println("Path being watched: " + watchKey.watchable());
                System.out.println();

                if (watchKey.isValid()) {
                    for (WatchEvent<?> event : watchKey.pollEvents()) {
                        System.out.println("Kinds: " + event.kind());
                        System.out.println("Contnet: " + event.context());
                        System.out.println("Count: " + event.count());
                        System.out.println();
                    }

                    boolean valid = watchKey.reset();

                    if (!valid) {
                        // The watchKey is not longer registered
                    }
                }

            }

        } catch (IOException | InterruptedException e) {
            // TODO: handle exception
        }
    }

  当调用WatchService的take方法时,这个方法会阻塞当前线程,直到一个文件变化的事件发生。此时,一个WatchKey对象会被返回。WatchKey对象包含了所发生事件的相关信息。它的watchable方法返回的是所被观察的对象信息(例子中的D:/home/sample文件夹)。pollEvents方法返回了一个所有要被执行的事件列表。最后我们调用了watchKey的reset方法来将这个key设置成初始状态,接受新的响应事件。

  Java7中新增对于文件系统操作的API不少,在此只是列出了一些常会用到的功能,有一些API本人也没有完全理解,有待进一步的研究和学习。
分享到:
评论

相关推荐

    JDK7新特性(完整篇)

    1.4 JDK7新特性&lt;四&gt; NIO2.0 文件系统 . . . 1.5 JDK7新特性&lt;五&gt; fork/join 框架 . . . . . 1.6 JDK7新特性&lt;六&gt; 监听文件系统的更改 1.7 JDK7新特性&lt;七&gt; 遍历文件树 . . . . . . . 1.8 JDK7新特性&lt;八&gt;异步io/AIO ...

    Spring2.5的新特性

    &lt;br&gt;新发布的Spring2.5继续坚持了这个发展趋向,特别是为那些使用Java 5或更新版本java的开发人员提供了进一步简化而强大的新特性。这些新特性包括:注解驱动的依赖性注入(annotation-driven dependency ...

    java语言快速入门习题与答案

    Java的跨平台特性,是相对于其他编程语言而言的。...其中,Java运行系统又称Java虚拟机(JVM),它实现了Java解释器的功能,只要不同的操作系统上安装了对应版本的JVM,就可以解释执行Java字节码文件,运行Java程序。

    android核心分析pdf

    android核心分析,介绍了android的一些特性,共二十几个介绍点,网上word文件转化为pdf,便于阅读 Android核心分析(01)----讨之设计意图 Android核心分析(02)----方法论探讨之概念空间篇 Android核心分析(03)-...

    Visual C++实践与提高-COM和COM+篇『PDF』

    因文件超过20M不能上传,所以拆分为两个文件分次上传 第1章 COM背景知识 1.1 COM的起源 1.1.1 软件业面临的挑战 1.1.2 传统解决方案 1.1.3 面向对象程序设计方法 1.1.4 最终解决方案:组件软件 1.1.5 面向对象的...

    Java典型模块

    第1篇 Java开发必备基础 第1章 搭建Java开发环境 1.1 Java的过去、现在和未来 1.1.1 Java的历史 1.1.2 Java的语言特点 1.1.3 Java API简介 1.1.4 Java未来发展 1.2 Java程序设计环境 1.2.1 命令行工具——JDK 6.0 ...

    Java开源的下一代社区平台Symphony.zip

    万能的 GitHub 上连个能用的 Java 社区系统都找不到,Sym 填补了这个宇宙级空白 做最 NB 的开源社区系统,预计几年以后 82% 的社区都将是 Sym 搭建的 作者技痒,炫技之作,Ruby/Python/Node.js/(特别是)PHP ...

    学通Java的24堂课

    第1部分 基础篇 第1堂课 java概述 3 视频讲解:31分钟 1.1 java简介 4 1.1.1 java发展历史 4 1.1.2 java的几个版本 5 1.1.3 java应用领域 6 1.1.4 java项目成功案例 6 1.1.5 怎样学好java 8 1.2 环境搭建 8...

    Java JNI完全手册

    转: 最近在公司里做了一个手机的项目,需要JAVA程序在发送短信的时候和第三方的短信服务器连接。...通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法。

    JAVA在SQLite嵌入式数据库中的应用.rar

    SQLite 作为一个开源的嵌入式数据库产品,具有系统开销小,检索效率高的特性,适用于手机、PDA、机顶盒设备等电器,并且作为嵌入式数据库在可下载的消费类应用程序中运行的很好。这篇文章介绍嵌入式数据库产品SQLite...

    《程序天下:J2EE整合详解与典型案例》光盘源码

    8.4 JUnit的新特性 8.4.1 改变测试方法的命名方式 8.4.2 不再继承TestCase 8.4.3 改变初始化和销毁方式 8.4.4 改变异常处理的方式 8.5 小结 第九章 CVS使用指南 9.1 CVS介绍 9.1.1 CVS简介 9.1.2 为什么要使用CVS ...

    Java开发实战1200例(第1卷).(清华出版.李钟尉.陈丹丹).part3

    本书是第II卷,以开发人员在项目开发中经常遇到的问题和必须掌握的技术为中心,介绍了应用Java进行桌面程序开发各个方面的知识和技巧,主要包括Java语法与面向对象技术、Java高级应用、窗体与控件应用、文件操作...

    搞定J2EE:STRUTS+SPRING+HIBERNATE整合详解与典型案例 (2)

    8.4 JUnit的新特性 8.4.1 改变测试方法的命名方式 8.4.2 不再继承TestCase 8.4.3 改变初始化和销毁方式 8.4.4 改变异常处理的方式 8.5 小结 第九章 CVS使用指南 9.1 CVS介绍 9.1.1 CVS简介 9.1.2 为什么要使用CVS ...

    搞定J2EE:STRUTS+SPRING+HIBERNATE整合详解与典型案例 (1)

    8.4 JUnit的新特性 8.4.1 改变测试方法的命名方式 8.4.2 不再继承TestCase 8.4.3 改变初始化和销毁方式 8.4.4 改变异常处理的方式 8.5 小结 第九章 CVS使用指南 9.1 CVS介绍 9.1.1 CVS简介 9.1.2 为什么要使用CVS ...

    网管教程 从入门到精通软件篇.txt

    如果未指定文件系统,将使用现有的文件系统格式。  Map  显示驱动器号与物理设备名称的映射。该信息在运行 fixboot 和 fixmbr 命令时非常有用。  map 命令仅在使用故障恢复控制台时才可用。  Map [ arc]  ...

    搞定J2EE:STRUTS+SPRING+HIBERNATE整合详解与典型案例 (3)

    8.4 JUnit的新特性 8.4.1 改变测试方法的命名方式 8.4.2 不再继承TestCase 8.4.3 改变初始化和销毁方式 8.4.4 改变异常处理的方式 8.5 小结 第九章 CVS使用指南 9.1 CVS介绍 9.1.1 CVS简介 9.1.2 为什么要使用CVS ...

Global site tag (gtag.js) - Google Analytics