如何检查文件是否可读?

9
我正在编写一个Java 6应用程序,需要检查文件是否可读。然而,在Windows上,canRead()总是返回true。所以我认为,唯一的解决方案可能是基于WINAPI的本地解决方案,并使用JNA/JNI编写。
但是,还有另一个问题,因为很难在WINAPI中找到一个简单的函数来返回关于文件访问的信息。我找到了GetNamedSecurityInfo或GetSecurityInfo,但我不是高级WINAPI程序员,它们与JNA/JNI连接起来太复杂了。有什么想法来解决这个问题吗?

10
你可以尝试读取文件并查看是否出现错误。 - Peter Lawrey
4
相关链接:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6203387 - adarshr
2
正确性比速度更重要。 ;) 你不需要读整个文件,只需打开并关闭它即可。 - Peter Lawrey
4
就磁盘操作而言,打开和关闭文件所需的工作与读取与该文件相关联的安全描述符所需的工作基本相同。如果性能不相似,我会感到惊讶。 - arx
1
相反,像这样尝试预测未来并不是“最佳实践”。 - user207421
显示剩余5条评论
5个回答

11

Java 7引入了Files.isReadable静态方法,它接受一个文件Path并在文件存在并且可读时返回true,否则返回false。

来自文档

测试文件是否可读。该方法检查文件是否存在,并且此Java虚拟机具有允许其打开文件以进行读取的适当权限。根据实现,此方法可能需要读取文件权限、访问控制列表或其他文件属性,以便检查对文件的有效访问。因此,相对于其他文件系统操作,此方法可能不是原子性的。

请注意,此方法的结果会立即过期,不能保证后续尝试打开文件进行读取将成功(甚至无法访问同一文件)。在安全敏感的应用程序中使用此方法时应小心。

示例:

File file = new File("/path/to/file");
Files.isReadable(file.toPath()); // true if `/path/to/file` is readable

3
感谢您明确指出答案立即无用的事实。对于任何想要像其他“我需要在读取文件之前检查是否可以读取它”的答案一样做此类操作的人来说,这个答案是一个TOCTOU漏洞。(TOCTOU漏洞是指时间检查到使用期间的漏洞) - Andrew Henle
@AndrewMartin 我将使用它来帮助确保用户输入了有效的输出路径 这与检查文件是否可读(在您的情况下是可写的?)不同,目的根本不同。您正在验证路径名。 - Andrew Henle
@AndrewHenle 你说得对,我觉得我的评论没有用,所以我已经删除了它。 - vowel-house-might
@AndrewMartin 我认为这是一个非常有用的评论 - 你以一种创新的方式使用了 isReadable() 来验证路径名,其中“可读”部分实际上并不重要。 - Andrew Henle

8

尝试使用以下代码

public boolean checkFileCanRead(File file){
    try {
        FileReader fileReader = new FileReader(file.getAbsolutePath());
        fileReader.read();
        fileReader.close();
    } catch (Exception e) {
        LOGGER.debug("Exception when checking if file could be read with message:"+e.getMessage(), e);
        return false;
    }
    return true;
}

我不喜欢在控制结构的位置使用异常,但在这种情况下绕过它也不错... - aleroot
7
这里的存在和可读性检查只是多余的开销。显然,当您尝试打开文件时,它们必须发生:两次执行这些操作毫无意义。 - user207421

6

您可以使用FilePermissionAccessController来实现更强的安全性:

FilePermission fp = new FilePermission("file.txt", "read");
AccessController.checkPermission(fp);

如果允许请求的访问,checkPermission会默默返回。如果被拒绝,则会抛出AccessControlException异常。

1
这与安全有关还是与操作系统环境有关?如果与后者相关,那就非常棒了。 - James P.
始终意味着=适用于可读和不可读文件。有人遇到了同样的问题:https://dev59.com/jWbWa4cB1Zd3GeqPZ7wl - peter
9
这段内容的意思是:该代码段并不检查文件是否可读。它检查当前执行的Java代码是否有一个.policy文件条目,允许Java安全管理器授予权限尝试读取该文件:如果没有安装安全管理器,则始终会成功,这在此情况下可能是常见的。操作系统是否允许打开文件是完全不同的问题。这不是答案。 - user207421

4

只有在需要阅读文本时,才需要知道它是否可读。因此,在需要时尝试阅读,并在无法理解时进行处理。测试任何资源是否可用的最佳方法是尝试使用它,并随时处理异常或错误。不要试图预测未来。


所以最好的方法不是尝试读取文件属性,而是尝试读取文件,如果出现异常,则文件不可读... 这似乎不是最佳解决方案。 - aleroot
1
@aleroot 这只是毫无意义的教条主义,特别是你显然不知道什么才是“最佳解决方案”:否则你就不会在这里提问了。如果捕获异常可以提供你所需的状态,那就使用它。我在这里推荐的不是“变通方法”,而是正确的技术。其他任何方法都是重复和冗余的,或者是错误的,并且绝对是一种变通方法。 - user207421
1
@aleroot,那么你的“最佳实践”替代方案是什么?你在和什么进行比较?为什么你一直回避这个问题?当调用DataInputStream.readUTF()ObjectInputStrream.readObject()时,你对捕获EOFException有什么替代方案? - user207421
对我来说最好的解决方案是 OP 指出的:如果文件存在,则读取文件的属性,如果它具有读取属性,则继续读取文件,在某些情况下出现问题时捕获异常... - aleroot
2
@aleroot 这遭受了我之前指出的所有问题的困扰:总结来说,就是时间窗口和冗余,即低效率。而且你还没有回答我的其他问题。看起来这些讨论总是走同样的路线:一个有理有据的案例被视为纯粹的“观点”,而不支持的盲目应用接受的教条则被认为是“最佳实践”。不要过度投资于编程时尚。当你在这个领域待久了,你会意识到下一种时尚很快就会出现,而且它们经常是相互矛盾的。 - user207421
显示剩余3条评论

1
在实际使用某个资源之前,检查该资源的可访问性(在此情况下为文件的可读性)是完全合理的。
想象一下,有一个服务器应用程序,将使用一个子组件,在某些情况下稍后读取特定文件。在服务器启动时检查文件的可读性并发出警告可能很有用。然后,某人可以在这些情况导致子组件尝试读取该文件之前解决问题(使文件可读等)。
当然,这种预先检查不能替代子组件中适当的异常处理(很可能在开始时文件是可读的,但后来变得不可读)。
因此,我认为关于预检查的问题是非常合理的。
至于检查资源的方法,请尽量与实际使用相似。如果稍后将读取文件,则尝试读取它!
至于Files.isReadable(...):不能保证Files.isReadable(...)下面的文件系统提供程序没有错误。它可能返回true,但如果实际上读取文件,则会抛出异常。
当然,如果您正在编写文件管理器应用程序,则请使用Files.isReadable(...),但我想那不是这种情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接