Pathосные новшества JDK7. Работа с файловой системой

Данная статья является вольным переводом с авторскими комментариями учебного пособия компании Oracle, по работе с системой ввода-вывода, файлами, папками, реализованной в пакете java.nio.file. Сами разработчики предупреждают, что информация находится в процессе разработки и коррекции ошибок(в ожидании релиза JDK7), и предоставляется нам как ознакомительная, что, в прочем, не мешает нам использовать ее уже сейчас. Чтобы вывести изложение из чисто теоретического ключа, к более практическому, я сфокусирую внимание на тех «вкусностях», что помогут желающим написать, н-р, свой файловый менеджер. Итак, опустив вводную про то, что такое имя файла, структура файловой системы и прочее, приступим.

Класс Path


Прежде чем перейти к более интересному материалу, необходимо рассмотреть базовый класс Path.
Класс Path включает в себя различные методы, которые могут быть использованы для получения информации о пути, получения доступа к элементам пути, преобразования пути в другие формы, извлечения части пути. Существуют также методы для сравнения строк пути и методы для удаления избыточности.

Для создания экземпляра класса Path, воспульзуемся статическим методом get класса Paths, позволяющего создать путь из строки или URI.
Path path1 = Paths.get("/home/iam/folder");<br/>
Path path2 = Paths.get("C:\\this\\my\\folder");<br/>
Path path3 = Paths.get("file:///Users/user/myfile.txt");


После того как экземпляр Path создан, мы можем получить некоторую информацию о пути:

//Path path = Paths.get("C:\\home\\joe\\foo");    // Microsoft Windows syntax<br/>
Path path = Paths.get("/home/iam/folder");       // Solaris syntax<br/>
System.out.format("toString: %s%n", path.toString());              //-->/home/iam/folder<br/>
System.out.format("getName: %s%n", path.getName());           //-->folder<br/>
System.out.format("getName(0): %s%n", path.getName(0));    //-->home<br/>
System.out.format("getNameCount: %d%n", path.getNameCount());  //-->3<br/>
System.out.format("subpath(0,2): %d%n", path.subpath(0,2)); <br/>
System.out.format("getParent: %s%n", path.getParent());        //-->/home/iam/<br/>
System.out.format("getRoot: %s%n", path.getRoot());              //-->/<br/>
System.out.format("isHidden: %s%n", path.isHidden());           //-->false

метод subpath остался без вывода не случайно. Он упорно не хочет работать ни в Win7 ни Ubuntu, о чем я сообщил куда надо. Хотя этот пример взят с оригинального туторала, а в прочем это не единственная ошибочка.

Файлы и атрибуты файлов. Управление метаданными.


Определение «метаданные» можно истолковать как «данные о других данных.» В контексте работы с файловой системой, это могут быть данные, содержащиеся в файлах и каталогах, а также метаданные, представляющие информацию о каждом из этих объектов: является ли объект обычным файлом, директорией, или представляет собой ссылку? Каков его размер, дата создания, дата последнего изменения, кто владелец файла, какая группа владельца, и какие права доступа?
Пакет java.nio.file.attribute предоставляет API для управления метаданными файловой системы, или, как это обычно называется, — атрибутов файлов. Поскольку разные файловые системы имеют разные понятия о том, какие атрибуты должны быть отслежены, наиболее общие атрибуты файла объединяются в так называемые представления. Такое представление отображается с учетом специфики конкретной реализации файловой системы, н-р, POSIX или DOS, или на основе общих функций, таких как информация о владельце файла.
Поддерживаются следующие представления:
  • BasicFileAttributeView – базовые атрибуты, поддерживаемые всеми реализациями файловых систем.
  • DosFileAttributeView – расширяет базовые атрибуты, добавлением стандартных четырех бит, которые используются системами, поддерживающими атрибуты DOS.
  • PosixFileAttributeView – поддержка атрибутов стандарта POSIX. Атрибуты включают в себя владельца файла( file owner), группу(group owner), и уровни прав доступа(access permissions).
  • FileOwnerAttributeView – поддерживается всеми системами, реализующими концепцию владельца файла.
  • AclFileAttributeView – поддерживает чтение и модификацию списков управления доступом файла( Access Control Lists (ACL)). Поддерживается модель NFSv4 ACL. Любая модель ACL, такая как Windows ACL, имеющая проработанную систему отображения к модели NFSv4, также должна поддерживаться.
  • UserDefinedFileAttributeView – реализация поддержки пользовательских метаданных.

П-р:
import java.io.IOException;<br/>
import java.nio.file.Path;<br/>
import java.nio.file.Paths;<br/>
import java.nio.file.attribute.*;<br/>
 <br/>
public class Main {<br/>
 <br/>
    /**<br/>
     * @param args the command line arguments<br/>
     */
<br/>
    public static void main(String[] args) throws IOException{<br/>
        // TODO code application logic here<br/>
        Path file = Paths.get("/home/aum/Downloads/demotivatory_15.jpg");<br/>
        BasicFileAttributes attr = Attributes.readBasicFileAttributes(file);<br/>
 <br/>
if (attr.creationTime() != null) {<br/>
    System.out.println("creationTime: " + attr.creationTime());<br/>
}<br/>
if (attr.lastAccessTime() != null) {<br/>
    System.out.println("lastAccessTime: " + attr.lastAccessTime());<br/>
}<br/>
if (attr.lastModifiedTime() != null) {<br/>
    System.out.println("lastModifiedTime: " + attr.lastModifiedTime());<br/>
}<br/>
 <br/>
System.out.println("isDirectory: " + attr.isDirectory());<br/>
System.out.println("isOther: " + attr.isOther());<br/>
System.out.println("isRegularFile: " + attr.isRegularFile());<br/>
System.out.println("isSymbolicLink: " + attr.isSymbolicLink());<br/>
System.out.println("size: " + attr.size());<br/>
 <br/>
    }<br/>
 <br/>
}

Результат:
run:
lastAccessTime: 2011-02-14T14:16:32Z
lastModifiedTime: 2011-02-04T02:17:27Z
isDirectory: false
isOther: false
isRegularFile: true
isSymbolicLink: false
size: 64613
BUILD SUCCESSFUL (total time: 0 seconds)


На этом вводную предлагаю считать завершенной. За бортом осталось множество методов «синтаксической» работы с Path: удаление избыточности в пути, преобразование к абсолютному пути, сравнение путей, создание путей из объеденения и т.п. Всю необходимую информацию вы можете найти здесь (класс Path) И теперь мы смело можем перейти к более сложному, но и более интересному материалу.

Обход дерева файлов


Получение информации о папках и файлах на диске, довольно типичная задача для прикладных программ. Пакет java.nio.file предлагает нам удобное решение такой задачи, предоставляя интерфейс FileVisitor.
FileVisitor определяет требуемое поведение в ключевых точках прохождения процесса: когда файл посещен, прежде чем получить доступ к каталогу, после получения доступа к каталогу, или в случае отказа. Интерфейс состоит из пяти(! оставлю слово пять из оригинального текста, хотя как не старался нашел только 4! метода ) методов, которые соответствуют этим ситуациях:

  • preVisitDirectory – вызывается до входа в папку
  • postVisitDirectory – вызывается после «просмотра» всех объектов каталога. В случае возникновения ошибки, вызывается исключение и передается методу
  • visitFile – вызывается для получения информации о файле, обрабатываемом в данный момент. В метод передаются атрибуты файла BasicFileAttributes, или мы можем передать определенный набор атрибутов, н-р можем выбрать чтение атрибутов DosFileAttributeView, чтобы определить является ли файл скрытым(«hidden»).
  • visitFileFailed – вызывается при невозможности получить доступ к файлу. В этом случае вызывается исключение и передается методу. Обработка этого события может быть различной(вызов исключения, запись в лог или вывод информации на консоль и т.д.


П-р:
import java.nio.file.FileVisitOption;<br/>
import java.util.EnumSet;<br/>
import java.nio.file.Files;<br/>
import java.nio.file.Paths;<br/>
import java.io.IOException;<br/>
import java.nio.file.FileVisitResult;<br/>
import java.nio.file.Path;<br/>
import java.nio.file.SimpleFileVisitor;<br/>
import java.nio.file.attribute.BasicFileAttributes;<br/>
import static java.nio.file.FileVisitResult.*;<br/>
 <br/>
//Используем класс SimpleFileVisitor, который реализовывает методы интерфейса FileVisitor<br/>
public  class PrintFiles extends SimpleFileVisitor<Path> {<br/>
    //Выводим информацию о обрабатываемом в данный момент файле.<br/>
// метод Files.probeContentType выводит информацию о типе контента<br/>
    @Override<br/>
    public FileVisitResult visitFile(Path file, BasicFileAttributes attr) throws IOException {<br/>
        if (attr.isSymbolicLink()) {<br/>
            System.out.format("Symbolic link: %s ", file);<br/>
        } else if (attr.isRegularFile()) {<br/>
            System.out.format("Regular file: %s Content is %s%n ", file,Files.probeContentType(file));<br/>
 <br/>
        } else {<br/>
            System.out.format("Other: %s ", file);<br/>
        }<br/>
        System.out.println("(" + attr.size() + "bytes)");<br/>
        return CONTINUE;<br/>
    }<br/>
 <br/>
    //Выводим информацию о посещенном каталоге<br/>
    @Override<br/>
/* Перечисление FileVisitResult имеет следующие варианты<br/>
CONTINUE продолжить проход.<br/>
SKIP_SIBLINGS продолжить проход без осмотра дочерних папок.<br/>
SKIP_SUBTREE продолжить без просмотра объектов данной папки.<br/>
TERMINATE заверщить.<br/>
*/
<br/>
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) {<br/>
        System.out.format("Directory: %s%n", dir);<br/>
        return CONTINUE;<br/>
    }<br/>
    //в случае ошибки доступа к файлу выбрасывается исключение IOException<br/>
       @Override<br/>
    public FileVisitResult visitFileFailed(Path file, IOException exc) {<br/>
        System.err.println(exc);<br/>
        return CONTINUE;<br/>
    }<br/>
    public static void main(String[] args) throws IOException{<br/>
        //создаем объект Path<br/>
        Path startingDir = Paths.get("/home/aum/myjobs/");<br/>
        //создаем экземпляр нашего класса, реализующего FileVisit<br/>
        PrintFiles pf = new PrintFiles();<br/>
 //создаем экземпляр EnumSet, необходимый нам как параметр, и указывающий,<br/>
// что программа при  прохождении дерева файлов, следует по ссылкам<br/>
        EnumSet<FileVisitOption> options = EnumSet.of(FileVisitOption.FOLLOW_LINKS);<br/>
       int maxDepth = 2; //максимальное число уровней каталога для просмотра<br/>
       /* Запуск анализа дерева файлов. Используется один из методов класса Files*/<br/>
       Files.walkFileTree(startingDir, options, maxDepth, pf);<br/>
 <br/>
    }<br/>
}

Результат
run:
Regular file: /home/aum/myjobs/sys.txt Content is text/plain
(11362bytes)
Regular file: /home/aum/myjobs/vzriv.mp3 Content is audio/mpeg
(2336827bytes)
Regular file: /home/aum/myjobs/report.html Content is text/html
(24574bytes)
Regular file: /home/aum/myjobs/examples.desktop Content is application/x-desktop
(179bytes)
Directory: /home/aum/myjobs/java
Directory: /home/aum/myjobs
BUILD SUCCESSFUL (total time: 0 seconds)


И в завершении о производительности. Н-р, код указаный выше, выполняется (на моем каталоге /home) так:
Objects found 19931 Total Size 2329851621 byte
BUILD SUCCESSFUL (total time: 38 seconds)
системные характеристики
Ubuntu 10.4
Kernel Linux 2.6.32-28
Gnom 2.30.2
Memory 1.9 G
Intel® Core(TM) i3 CPU M330 @ 2.13 GHz


Более полную информацию можно получить здесь
Метки:
java jdk7 Path nio