最后更新:2015-04-15
这个Java NIO的Files类(java.nio.file.Files)提供了数个方法中文件系统中操作文件。这个Java NIO的Files类教程将会覆盖这些方法的大部分通用方法的使用。这个类包含了许多的方法,所以也可以核对这个JavaDoc,如果你需要一个没有在这里描述的方法。这个Files类只是可能为了它仍然有一个方法。
这个java.nio.file.Files类同java.nio.file.Path实例一起工作,以至于你在同Files类工作之前需要理解这个Path类。
Files.exists()
这个方法检查给予的这个路径是否在这个文件中。
去创建一个在文件系统不存在的Path实例是可能的。例如,如果你计划创建一个新的目录,你将会首先创建相应的Path实例,然后创建这个目录。
因为Path实例可能指向或者可能没有指向存在这个文件系统的路径,你可以使用Files.exists()方法去决定是否他们是(一旦你需要检查那个)。
这里有一个Java的Files.exists()实例:
Path path = Paths.get("data/logging.properties"); boolean pathExists = Files.exists(path, new LinkOption[]{ LinkOption.NOFOLLOW_LINKS});
这个例子首先创建一个指向这个路径的一个Path实例,我们去检查他是否存在。第二,这个例子使用Path实例作为第一个参数,调用这个Files.exists()方法。
注意Files.exists的第二个参数。这个参数是一个数组选项,它影响着这个Files.exists是怎么决定文件路径是否存在。在上面的这个例子中,这个数组包括这个LinkOption.NOFOLLOW_LINKS,这个意味着这个Files.exists方法应该不会跟随这个文件系统的符号链接去决定路径是否存在。
Files.createDirectory()
这个方法创建一个来自于Path实例的一个新的目录。这里有一个例子:
Path path = Paths.get("data/subdir"); try { Path newDir = Files.createDirectory(path); } catch(FileAlreadyExistsException e){ // the directory already exists. } catch (IOException e) { //something else went wrong e.printStackTrace(); }
第一行创建了一个将会去创建的一个目录的Path实例。在try-catch块内部的这个Files.createDirectory()方法使用这个path作为一个参数被调用。如果创建一个目录成功,一个执行这个新创建的路径的Path实例被返回。
如果这个目录已经存在了,一个java.nio.file.FileAlreadyEXistsException将会抛出。如果其他的出现错误的话,一个IOException可能会抛出,例如,如果所期望的上级目录,新目录不存在的话,一个IOException会抛出。这个上级目录是你想创建的那个新的目录。因此,它意味着这个新目录的上级目录。
Files.copy()
这个方法拷贝一个文件从一个路径到另外一个。这里有一个例子:
Path sourcePath = Paths.get("data/logging.properties"); Path destinationPath = Paths.get("data/logging-copy.properties"); try { Files.copy(sourcePath, destinationPath); } catch(FileAlreadyExistsException e) { //destination file already exists } catch (IOException e) { //something else went wrong e.printStackTrace(); }
首先,这个例子创建了一个源和目的的Path实例。然后这个例子调用了Files.copy()方法,通过这两个Path实例作为参数。这个将会导致通过源路径的文件引用被拷贝到被目录路径的文件引用中。
如果这个目的文件已经存在,一个java.nio.file.FileAlreadyEXistsException将会抛出。如果其他的事情出现错误,一个IOException会抛出。例如,如果拷贝文件的这个目录不存在,将会抛出一个IOException。
覆盖已经存在的文件
强制的Files.copy()去覆盖已经存在的文件是可能的。这里有一个例子展示怎样通过Files.copy()方法去覆盖已经存在的文件:
Path sourcePath = Paths.get("data/logging.properties"); Path destinationPath = Paths.get("data/logging-copy.properties"); try { Files.copy(sourcePath, destinationPath, StandardCopyOption.REPLACE_EXISTING); } catch(FileAlreadyExistsException e) { //destination file already exists } catch (IOException e) { //something else went wrong e.printStackTrace(); }
注意Files.copy()方法中的第三个参数。这个参数指明如果目的文件已经存在将会覆盖。
Files.move()
这个Java NIO类也包含了一个这样的函数,移动一个文件从一个路径到另外一个路径。移动一个文件就像重命名它一样,移动一个文件既可以移动到不同的目录,还可以同时改变他的名字。是的,这个java.io.File类的renameTo()方法也可以做这个事情,但是现在你也可以在java.nio.file.Files类中做文件移动的功能。
这里有一个例子:
Path sourcePath = Paths.get("data/logging-copy.properties"); Path destinationPath = Paths.get("data/subdir/logging-moved.properties"); try { Files.move(sourcePath, destinationPath, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { //moving file failed. e.printStackTrace(); }
首先源路径和目的路径被创建。这个源路径指向了将要移动的文件,以及整个目的路径指向了这个文件将要移动的目的地。然后Files.move方法被调用了。这个就会导致这个文件将会被移动。
注意这个方法里面的第三个参数,这个参数告诉这个方法将会覆盖目的路径中任何存在的文件。这个参数实际上是可选择的。
如果移动文件失败可能会抛出IOException异常。例如,如果一个文件已经存在目的路径中,并且你遗漏了StandardCopyOption.REPLACE_EXISTING这个选项,或者将要移动的这个文件不存在等等。
Files.delete()
这个方法可以删除一个文件或者一个目录。这里有一个例子:
Path path = Paths.get("data/subdir/logging-moved.properties"); try { Files.delete(path); } catch (IOException e) { //deleting file failed e.printStackTrace(); }
首先创建一个指向要删除的文件的Path实例。然后调用这个删除的方法。如果因为一些原因(例如文件或者目录不存在)删除失败,一个IOException将会抛出。
Files.walkFileTree()
这个方法包含了可以递归遍历一个目录树的功能。这个方法携带着一个Path实例和FileVisitor作为参数。这个Path实例指向了你想要遍历的目录。这个FileVisitor是中遍历期间被调用。
在我解释这个遍历是如何工作的之前,这里首先有一个FileVisitor接口:
public interface FileVisitor { public FileVisitResult preVisitDirectory( Path dir, BasicFileAttributes attrs) throws IOException; public FileVisitResult visitFile( Path file, BasicFileAttributes attrs) throws IOException; public FileVisitResult visitFileFailed( Path file, IOException exc) throws IOException; public FileVisitResult postVisitDirectory( Path dir, IOException exc) throws IOException { }
你不得不自己去实现FileVisitor接口,以及传递你实现的一个实例到walkFileTree方法中。你的FileVisitor实现的每一个方法中目录遍历期间的不同时间将会被调用。如果你不想去实现这些所有的方法,你可以继承SimpleFileVisitor类,这个类里面包含了FileVisitor接口所有方法的默认实现。
这里有一个walkFileTree的例子:
Files.walkFileTree(path, new FileVisitor<Path>() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { System.out.println("pre visit dir:" + dir); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { System.out.println("visit file: " + file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { System.out.println("visit file failed: " + file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { System.out.println("post visit directory: " + dir); return FileVisitResult.CONTINUE; } });
在FileVisitor实现的每一个方法中目录遍历期间的不同时间将会被调用:
这个preVisitDirectory()
方法将会中访问任何目录之前会被调用。这个 postVisitDirectory()
方法是中访问目录之后被调用。
这个visitFile()方法是中每一个文件访问期间会被调用,它对于目录是不会被调用的--只是相对文件。这个visitFileFailed()方法一旦访问文件失败将会被调用。例如,如果你没有正确的权限,或者是其他事情出错了。
四个方法中的每一个都返回了一个FileVisitResult
枚举实例。这个枚举实例包含下面四个选项:
- CONTINUE
- TERMINATE
- SKIP_SIBLINGS
- SKIP_SUBTREE
通过返回的值去决定接下来这个方法应该怎么走。
CONTINUE意味着意味着这个文件将会正常的继续访问下去。
TERMINATE意味着这个文件的访问现在将会终止。
SKIP_SIBLINGS意味着这个文件的访问将会继续,但是不会访问这个文件或者目录的兄弟节点。
SKIP_SUBTREE意味着这个文件的访问将会继续,但是不会访问这个目录的条目。这个值只是如果从preVisitDirectory()
返回就会有这个功能。如果从其他任何方法返回,他将会作为一个CONTINUE被解释使用。
搜索文件
这里有一个walkFileTree方法,它继承了SimpleFileVisitor
去寻找README.txt的文件:
Path rootPath = Paths.get("data"); String fileToFind = File.separator + "README.txt"; try { Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { String fileString = file.toAbsolutePath().toString(); //System.out.println("pathString = " + fileString); if(fileString.endsWith(fileToFind)){ System.out.println("file found at path: " + file.toAbsolutePath()); return FileVisitResult.TERMINATE; } return FileVisitResult.CONTINUE; } }); } catch(IOException e){ e.printStackTrace(); }
递归的删除目录
walkFileTree方法也可以用来删除所有文件的一个目录以及它里面的子目录。如果它是空的,这个Files.delete()方法将只是删除一个目录。通过穿行所有的目录以及删除所有的文件(在visitFile()里面)在每一个目录中,并且删除目录本身之后(在postVisitDirectory()里面),你可以删除一个带有所有子目录和文件的一个目录。这里有一个递归删除目录的一个例子:
Path rootPath = Paths.get("data/to-delete"); try { Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { System.out.println("delete file: " + file.toString()); Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { Files.delete(dir); System.out.println("delete dir: " + dir.toString()); return FileVisitResult.CONTINUE; } }); } catch(IOException e){ e.printStackTrace(); }
在Files类里面的额外的方法
这个java.nio.file.Files类包含了很多其他有用的功能,例如像创建符号链接的功能,决定文件大小的功能,设置文件权限的功能等等。你可以去查看这个类的Java文档去获取这些方法的更多信息。
翻译地址:http://tutorials.jenkov.com/java-nio/files.html