赞
踩
java 文件服务器api
Java 7为该语言引入了许多有用的功能,其中包括一个新的I / O文件包,与使用旧的java.io包相比,该文件包对文件系统功能(尤其是基于POSIX的系统)提供了更好的粒度控制。 本文将首先介绍新的API,然后使用一个称为WebFolder的基于Web的文件管理器项目的示例对它进行更详细的研究。 该项目提供了一种用于管理远程计算机上的文件系统的机制。 它支持诸如在文件系统上导航以及检查,重命名,复制或删除文件的操作。 使用新的I / O文件,我们可以扩展项目的功能,以处理ZIP存档内容并监视修改。 可以从http://webfolder.sf.net免费下载。
尽管基本文件操作API确实在版本之间进行了一些更新,但是对于Java 7,Java团队决定提供具有新设计的替代软件包,以新的方式覆盖文件系统操作。
基本文件操作API驻留在包java.nio.file中,带有两个子包java.nio.file.attribute和java.nio.file.spi。 新的API将文件相关的操作与java.io包分开,并且还提供了其他方法,目的是使文件系统的管理更简单。 从概念上讲,新的API构建为一组实体接口,覆盖文件系统的基本对象,而操作类覆盖文件系统本身的操作。 该概念继承自java.util包,其中的Collections和Arrays类分别对基本聚合数据结构(作为集合和数组)提供许多操作。 新软件包包含名称不同的基类和接口,以避免任何混淆,尤其是在java.io时。 和java.nio.file软件包一起使用。
新软件包不仅具有支持文件和文件系统操作的类的不同组织,而且还扩展了API的功能,例如提供了一种更简单的复制和移动文件的方法。
下表简要概述了两个软件包的基类和接口
Java <7 java.io, javax.swing.filechooser | Java> = 7 java.nio.file | 评论 |
文件 | 路径和文件 | 尽管File同时提供了文件位置和文件系统操作,但新的API将其分为两部分。 路径仅提供文件位置,并支持其他与路径相关的操作,而文件支持文件操作,具有许多文件中不可用的新功能,例如复制或读取整个文件的内容,或设置所有者。 |
文件系统视图 | 文件系统 | FileSystemView提供了基础文件系统的视图,仅在Swing文件选择器的上下文中使用。 FileSystem可以代表本地或远程定义的不同文件系统,也可以代表其他存储机制,例如ISO映像或ZIP存档。 FileSystem包含提供具体实现不同接口(如Path)的工厂。 |
无模拟 | 文件存储 | 表示文件存储的某些属性,例如总大小。 可以从特定路径或文件系统中检索它。 |
除了对象和操作的组织不同之外,新的文件系统API还能在大多数方法和构造函数中利用相对较新的Java功能,例如自动装箱,因此新API更干净,更易于使用。
在以下各节中,我们将详细介绍特定的改进。
新的文件包引入了一种遍历文件系统的新方法,与旧的基于数组和过滤器的版本相比,该方法旨在提高内存效率。 此外,新方法还可以深入遍历文件系统。 新的实现利用了Visitor设计模式 。 尽管您可以使用带有常规基于文件的遍历操作的过滤器来模仿“访客”模式,但要提供一种简单且具有存储效率的多级遍历算法却要困难得多。
访客模式作为接口FileVisitor引入。 由于该接口是通用的,因此您可能希望可以将其用于通过基于File的实现遍历文件系统,但是新的I / O文件实现要求仅将其用于Path继承的对象。 该接口声明了四个方法,其中SimpleFileVisitor提供了可从其继承的接口的实现,以便实现给定用例所需的任何方法。 下表简要概述了FileVisitor方法及其在SimpleFileVisitor中的行为:
名称 | 目的 | 默认 |
visitFile | 除非定义了过滤,否则为每个遍历的普通文件(包括符号链接)调用。 可以在此处处理任何与文件相关的有意义的操作,例如备份或搜索文件中的内容。 可以决定是停止还是继续运行。 目录不调用该方法。 | 返回CONTINUE |
preVisitDirectory | 如果访问的项目是目录而不是文件,则将调用此方法而不是visitFile。 它允许跳过特定目录的遍历,或在目标位置创建相应目录以进行复制操作。 | 返回CONTINUE |
postVisitDirectory | 遍历整个目录后,将调用该方法。 这是通过目录完成操作的便捷方法。 例如,如果遍历的目的是删除其所有文件,则可以使用此方法删除目录本身。 | 返回CONTINUE |
visitFileFailed | 如果在遍历文件系统期间发生任何未处理的异常,则将调用此方法。 如果重新抛出异常,则将停止所有遍历,并使用Files.walkFileTree将异常传播到启动文件系统遍历的代码。 可以在此处分析异常,并可以决定继续遍历。 | 重新抛出IOException |
如您所见,它是一个非常强大的界面,支持文件系统上的大多数常规用例,包括归档,搜索,备份或删除文件。 异常处理也非常灵活。 但是,如果您只需要获取某个目录的内容而不进行深入遍历,并且对旧的File.list()操作感到满意,那么新的IO Files也可以使用类似的功能,尽管它会返回一个集合。而不是简单的数组。
尽管新IO文件提供的文件系统遍历和组操作的可能性确实有用,但标准java.io也支持它们。 但是,此外,新的IO文件提供了旧软件包不支持的特定于操作系统的功能。 这种功能的一个重要示例是使用链接和符号链接,它们现在可以在任何文件系统遍历操作中创建和/或处理。 它仅适用于支持它们的文件系统-在其他情况下,将引发UnsupportedOperationException。 另一个扩展与管理文件属性(所有者和权限)有关。 同样,如果基础文件系统不支持它们,则将抛出IOException或UnsupportedOperationException。 下表提供了链接和扩展文件属性操作的快速概述。 可以从Files类请求所有这些操作。
操作方式 | 目的 | 评论 |
createLink | 创建映射到特定文件的硬链接 | |
createSymbolicLink | 创建映射到文件或目录的符号链接 | |
getFileAttributeView | 以FileAttributeView的文件系统特定实现的形式访问文件属性 | 尽管此方法提供了提供非预定义属性集的灵活性,但它仍然需要使用基础的特定类实现,因此,限制了代码的可移植性 |
getOwner | 获取文件的所有者 | 仅在支持owner属性的文件系统上工作 |
getPosixFilePermissions | 获取文件权限 | POSIX系统专用 |
isSymbolicLink | 指示给定路径是否为符号链接 | 特定于文件系统 |
readSymbolicLink | 读取符号链接的目标路径 | 特定于文件系统 |
readAttributes | 读取文件属性 | 该方法有两种变体,以不同形式返回属性 |
setAttribute | 设置文件属性 | 属性名称可能包含FileAttributeView限定符 |
当您计划使用表中提供的操作时,请参考新的IO文件API 文档 。
该API还提供了监视机制,因此可以监视特定文件或目录的状态,以了解诸如创建,修改或删除之类的事件。 不幸的是,没有可保证的用于观看事件的推送模型,并且在大多数情况下,应使用轮询机制,这在我看来会使实现的吸引力降低。 Watching服务还取决于系统,因此您不能使用该服务构建真正的可移植应用程序。 有5个界面涵盖观看功能。 下表快速概述了接口及其用法。
接口 | 目的 | 用法 |
看得见的 | 可以在监视服务中注册此类型的对象。 获得了一个监视键,可用于监视修改事件 | 必须获取接口的具体实现,以注册对与该对象有关的监视事件的兴趣。 请注意,接口Path是Watchable |
WatchService | 文件系统中的一种服务,用于注册Watchable对象,然后使用WatchKey监视修改 | 可以从FileSytem对象中检索AWatchService |
监视键 | 注册回执,用于轮询修改事件 | 可以存储该对象,然后将其用于轮询修改事件。 当有修改事件可用时,也可以直接从WatchService获取 |
WatchEvent | 进行观看活动 | WatchEvent对象在事件通知调用中传递,可以从中检索事件的种类和受影响的对象路径 |
WatchEvent.Kind | 携带观看赛事种类信息 | 用于指定您在注册Watchable时感兴趣的特定事件类型。 WatchEvent还在通知调用中提供了它 |
我会强调两种可能使用监视服务的方案。 一种是只需要监视特定对象的修改。 在这种情况下,可以在监视服务中注册Watchable对象以获得监视键,然后将监视键用于轮询修改事件。 针对监视键的轮询机制不会阻塞,因此,即使没有新事件发生,在轮询时仍可以返回空事件列表。 您可以在两次轮询之间引入延迟以减少轮询开销,以换取在发生通知事件时失去一些精度。 第二种情况利用监视服务的监视机制,适用于轮询与多个可监视对象有关的修改事件。 与第一种情况一样,您需要注册所有监视对象,但是可以忽略返回的监视键。 代替监视键轮询机制,使用服务轮询机制来针对已触发的修改事件检索监视键,然后使用针对监视键的轮询操作来处理事件。 在这种情况下,保证监视键已分配了一些事件。 单个线程可用于管理所有监视键。 监视服务轮询机制更加灵活,因为它支持阻塞,非阻塞和具有超时操作的阻塞。 结果,它也可以更准确。 由于前面提到的WebFolder项目使用它,因此稍后我们将看到第二种情况的示例。
新I / O文件的下一个主要功能是一组实用程序方法。 它使程序包自给自足,因为在大多数用例中,不需要标准java.io程序包中的其他功能。 输入流,输出流和字节通道可以使用Files方法直接获得。 该API支持完整的操作,例如复制或移动文件。 此外,整个文件内容现在可以作为字符串列表或字节数组读取。 但是请注意,大小控制没有参数,因此必须添加一些文件大小调查,以避免可能的内存问题。
最后,文件系统和存储是新I / O文件包的重要组成部分。 如我们所见,包的关键要素是文件位置由Path接口表示。 您需要使用FileSystem工厂获得该接口的具体实现,而该工厂又必须从FileSystems工厂获得。 下图显示了新I / O文件的关键元素之间的关系。
可以从文件系统中的特定文件(路径)中检索存储信息。
所有文件系统实现均由相应的提供程序备份,这些提供程序的基本类在包java.nio.file.spi中定义。 服务提供商的概念使开发人员可以轻松扩展文件系统的覆盖范围。 打包了一些有趣的文件系统提供程序,例如,一种转换ZIP文件的内容,从而允许大多数标准操作,例如遍历内容,创建,删除和修改文件。 我们稍后将看到一个示例。
我们对新IO文件的概述将不完整,而无需提及实现对并发的高度了解,因此大多数操作对于并发环境都是安全的。 移动文件也可以是原子的。 通过获取SecureDirectoryStream接口的具体实现,也可以保护使用目录内容的安全。 在这种情况下,如果外部攻击者移动或修改了目录,所有与目录相关的操作将保持一致。 仅使用相对路径来实现此目的。
学习新事物的最好方法是进行实际编码。 上面提到的WebFolder基于Web的文件管理器最初是使用java.io开发的,因此我决定迁移现有项目以使用新的IO文件。 它帮助我更好地理解了I / O文件中的概念,并帮助我评估了它在其他更严重的项目中的特殊用途。 我故意在此处使示例代码保持较小,但是可以从项目Web页面下载完整的源代码。
1.获取一个目录的内容
- try (RequestTransalated rt = translateReq(getConfigValue("TOPFOLDER", File.separator),
- req.getPathInfo());
-
- DirectoryStream<Path> stream = Files.newDirectoryStream(rt.transPath);) {
-
- for (Path entry : stream) {
-
- result.add(new Webfile(entry, rt.reqPath)); // adding directory
- element info in model
-
- }
-
- } catch (Exception ioe) {
-
- log("", ioe);
-
- } // No finally block here since the API supports the AutoCloseable and the new try
- block syntax
本示例填充要由网页视图呈现的目录模型。 Files.newDirectoryStream用于获取目录内容迭代器。
2.深度遍历
- Path ffrom = ….
-
- Files.walkFileTree(ffrom, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
-
- new SimpleFileVisitor<Path>() {
-
- @Override
-
- public FileVisitResult preVisitDirectory(Path dir,
-
- BasicFileAttributes attrs)
-
- throws IOException {
-
- Path targetdir =
- fto.resolve(fto.getFileSystem().getPath(ffrom.relativize(dir).toString()));
-
- try {
-
- Files.copy(dir, targetdir,
- StandardCopyOption.COPY_ATTRIBUTES);
-
- } catch (FileAlreadyExistsException e) {
-
- if (!Files.isDirectory(targetdir))
-
- throw e;
-
- }
-
- return FileVisitResult.CONTINUE;
-
- }
-
- @Override
-
- public FileVisitResult visitFile(Path file, BasicFileAttributes
- attrs) throws IOException {
-
- Path targetfile = fto.resolve(fto.getFileSystem()
-
- .getPath(ffrom.relativize(file).toString()));
-
- Files.copy(file, targetfile,
- StandardCopyOption.COPY_ATTRIBUTES);
-
- return FileVisitResult.CONTINUE;
-
- }
-
- });
此代码将目录的内容复制到文件系统上的另一个位置。 preVisitDirectory负责复制目录本身。 由于目标可以是另一个文件系统,因此该示例是在保留目录结构的同时提取ZIP存档的全部内容或将目录结构放置在ZIP存档中的便捷方法。 COPY_ATTRIBUTES选项将所有源文件属性(包括时间戳)保留在目标文件中。
可以使用类似的实现方式来删除目录的整个内容,在这种情况下,必须使用方法postVisitDirectory而不是preVisitDirectory来删除目录内容后再删除目录本身。
- @Override
-
- public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException {
-
- if (e == null) {
-
- if (dir.getParent() != null) {
-
- Files.delete(dir);
-
- return FileVisitResult.CONTINUE;
-
- } else
-
- return FileVisitResult.TERMINATE;
-
- } else {
-
- // directory iteration failed
-
- throw e;
-
- }
-
- }
本示例在删除目标目录之前先进行检查,以确保目标目录不在根目录下。 所有可能的异常都会传播给调用方以进行处理。
3. ZIP中的文件系统
- FileSystem fs = FileSystems.newFileSystem(zipPath, null);
-
- Path zipRootPath = fs.getPath(fs.getSeparator());
-
- ….
-
- Fs.close();
zipRootPath可用于出于任何目的启动对ZIP文件内容的遍历。 所获得的文件系统具有完整的功能,并且大多数操作(包括复制,移动和删除)都将起作用。 但是,监视服务不适用于ZIP文件系统。 另请注意,文件系统在使用后必须关闭。 如果在同一ZIP上打开另一个文件系统,则可能会观察到操作失败,因此请牢记这种可能性来编写代码。 但是,不需要关闭默认文件系统。 看来新的I / O文件包仅维护一个实例并负责并发性。
4.观看
使用监视服务的方法有多种,因此这里是前面已经提到的两种最常见方法的说明。
- WatchService ws = dir.getFileSystem().newWatchService();
-
- WatchKey wk = dir.register(ws, StandardWatchEventKinds.ENTRY_CREATE,
- StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
获取监视键后,可以将其传递给监视线程以监视相关事件
- @Override
-
- public void run() {
-
- for (;;) {
-
- if (watchKey != null) {
-
- for (WatchEvent<?> event : watchKey.pollEvents()) {
-
- updateScreen(event.kind(), event.context());
-
- }
-
- boolean valid = watchKey.reset();
-
- if (!valid) {
-
- break;
-
- }
-
- }
-
- }
如果不能足够快地消耗事件,那么将接收到事件类型OVERFLOW。 如果对事件没有更多兴趣,可以取消该监视键。 使用后也可以关闭手表服务。 另一种方法是在其中注册了多个可观察对象的情况下,使用监视服务方法来轮询修改事件。 此方法更适用于WebFolder应用程序。
- public void run() {
-
- for (;;) {
-
- try {
-
- WatchKey watchKey = watchService.take(); // poll(10, );
-
- processWatchKey(watchKey);
-
- } catch (InterruptedException ie) {
-
- break;
-
- }
-
- }
-
- }
获得了针对默认文件系统的监视服务,然后将其用于单个监视线程。 使用了take操作,由于它阻塞了,所以没有浪费的循环。 方法processWatchKey具有类似于上面提供的并与监视键事件相关联的实现,以支持轮询。 但是,这没有多余的循环,因为从监视服务获得的密钥已经具有关联的事件。
新的I / O文件提供:
1.强大的文件系统遍历机制,有助于执行复杂的组操作。
2.操纵特定的文件和文件系统对象及其属性,例如链接,所有者和权限。
3.一种方便的实用程序方法,可在读取,复制和移动整个文件内容时进行操作。
4.监视文件系统修改的监视服务。
5.文件系统上的原子操作提供了针对文件系统的进程同步。
6.在某些文件组织(如存档)上定义的自定义文件系统。
为什么要考虑将基于较旧的I / O程序包的系统迁移到较新的I / O程序有四个原因:
根据经验,如果其中两个或多个项目适用于您的项目,则可能值得进行迁移,否则,我建议您继续使用当前的实现。 不能移动的原因是,新的I / O文件实现不会使您的代码更紧凑或更具可读性。另一方面,在某些运行时实现中,首次访问时,新文件遍历操作可能会有些迟钝。 看来Windows的Oracle实现要进行大量的缓存,这在第一次访问时会花费大量时间。 但是,OpenJDK实现(IcedTea)在Linux上没有显示此问题,因此该问题似乎是特定于平台/实现的。
如果您决定迁移,下表为您提供了一些提示
当前 | 已迁移 | 评论 |
fileObj =新文件(新文件(pe1,pe2),pe3) | pathObj = fsObj。 getPath(pe1,pe2,pe3) | fsObj可以作为FileSystems.getDefault()获得,因为文件系统保留在Path本身中,所以可以从从相同文件系统获得的任何现有路径中检索它 |
fileObj.someOperation() | Files.someOperation(pathObj) | 在大多数情况下,操作名称是相同的,尽管可以添加与链接和属性有关的其他参数 |
fileObj.listFiles() | Files.newDirectoryStream(pathObj) | Files.walkFileTree应该用于深度遍历 |
新的FileInputStrean(file) | Files.newInputStream(pathObj) | 可以指定其他选项来打开文件 |
新的FileOutputStream(file) | Files.newOutputStream(pathObj) | 可以指定其他选项来打开文件 |
新的FileWriter(文件) | Files.newBufferedWriter(pathObj) | 可以指定其他选项来打开文件 |
新的FileReader(文件) | Files.newBufferedReader(pathObj) | 可以指定其他选项来打开文件 |
新的RandomAccessFile(文件) | Files.newByteChannel(pathObj) | 可以指定打开选项和文件创建属性 |
类File和interface Path有两种在它们之间转换的方法-pathObj.toFile()和fileObj.toPath()。 它可以帮助减少迁移工作,并且仅专注于新I / O文件提供的新功能。 作为迁移过程的一部分,可以考虑用Files.copy替换自定义文件复制实现。 Interface Path本身提供了许多方便的方法,这些方法可以减少以前基于Files对象的所需编码。 由于新代码将在Java 7及更高版本下运行,因此值得改善异常处理和资源释放。 下面的代码演示了旧的和新的机制:
- ClosableResource resource = null;
-
- try {
-
- Resource = new Resource(…);
-
- // resource processing
-
- } catch(Exception e) {
-
- } finally {
-
- if (resource != null)
-
- try {
-
- resource.close();
-
- } catch(Exception e) {
-
- }
-
- }
可以用更紧凑的代码代替
- try (Resource = new Resource(…);) {
-
- // resource processing
-
-
-
- } catch(Exception e) {
-
- }
资源必须实现接口AutoCloseable,而来自JRT的所有标准资源都是AutoCloseable。
翻译自: https://www.infoq.com/articles/java7-nio2/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1
java 文件服务器api
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。