[ BUG 记录] 薛定谔的 file :为什么文件明明存在但是 file.exists 却返回 false

[ BUG 记录] 薛定谔的 file :为什么文件明明存在但是 file.exists 却返回 false这是一个 java 项目,打包成可执行的 jar,在 Linux 服务器上通过 cronjob 定期执行。 某天客户反映说,程序无法正常导入文件。 首先,程序的日志里的错误信息是 FileNotFoundException,也就是说当程序调用 handleFile 方法,对传入…

记录一个项目中遇到的诡异 bug:

  • 这是一个 java 项目,打包成可执行的 jar,在 Linux 服务器上通过 cronjob 定期执行。
  • 每次执行,都会扫描服务器指定目录,如果目录中有文件,则进行处理,否则直接退出

下面是示例代码,很简单:

public class App {

    private static final String FOLDER = "/tmp/work";
    
    public static void main(String[] args) {
    	File folder = new File(FOLDER);
        File[] files = folder.listFiles();
        if (files != null && files.length > 0) {
            for (File file : files) {
                handleFile(file);
            }
        }
    }
}

某天客户反映说,程序无法正常导入文件。

拿到日志和导入失败的文件之后,开始 debug:

  • 首先,程序的日志里的错误信息是 FileNotFoundException,也就是说当程序调用 handleFile 方法,对传入的 File 对象进行读取时,发现文件找不到了.

  • 但是这个文件肯定是存在的,第一是客户可以通过 ls -l 看到,第二是程序可以通过 folder.listFiles 获取到。

  • 在本地搭建了和客户环境相同的测试环境之后,给程序加了日志,把那个文件放在目录下,发现 file.exists() 返回的是 false

为什么一个文件,明明存在,而且可以被 list 到,但是 file.exists() 却返回 false?

  • 接下来看这个文件,发现文件名里面有中文以及中文小括号,推测是字符编码的问题,改成全英文文件名就可以正常导入,但是客户的生产环境不能修改文件名规则,但基本确定是文件名里的中文和中文字符的问题。

但是客户反映,同样的文件,不用改启动脚本,重启了服务器就可以导入。

上哪儿说理去…

  • 接下来让客户列出可以正常导入时的系统环境变量,再列出无法正常导入时的系统环境变量

  • 发现当文件无法导入时,系统的 locale 变了…

后来让客户将系统的 locale 改成 zh_CN.UTF_8,问题解决,这也解释了为什么重启服务器可以解决这个问题,因为重启服务器系统会重新设置默认的 locale(UTF-8),而问题发生之前一定是有人改了系统的 locale…


这其中最迷惑的一点在于,我们在扫描某个文件夹下的文件进行处理时,通常认为 folder.listFiles() 返回的文件列表中的文件,都是“存在的”,但是通过这个 bug 可以看出,不一定

  • 首先,folder 一定要先存在,否则 folder.listFiles() 就会失败
  • 其次,文件一定也要真实存在,否则 folder.listFiles() 不会列出这个文件
  • 再次,folder.listFiles() 返回的只是这个文件夹下 由 folder 的路径 加每个文件的文件名 所组成的抽象路径对应的 file 对象
  • 当你真正的对每个 file 对象进行操作时,系统才会根据这个抽象路径去本地磁盘进行查找,但是如果这个时候你的系统不认识(错误的 locale)这个抽象路径里面的特殊字符,比如这个例子中的中文和中文小括号,那么系统就认为这个抽象路径(含特殊字符)所对应的文件不存在,于是你就得到了一个 FileNotFoundException

另外,file.exists() 方法的注释是这么写的:

    /** * Tests whether the file or directory denoted by this abstract pathname * exists. * * @return <code>true</code> if and only if the file or directory denoted * by this abstract pathname exists; <code>false</code> otherwise * * @throws SecurityException * If a security manager exists and its <code>{@link * java.lang.SecurityManager#checkRead(java.lang.String)}</code> * method denies read access to the file or directory */
    public boolean exists() {

Tests whether the file or directory denoted by this abstract pathname exists.

这句话的关键在于 Tests,即当真正去对这个 file 对象进行操作时,才会知道 file 对象代表的实际文件是否 真的 存在。


参考链接:

今天的文章[ BUG 记录] 薛定谔的 file :为什么文件明明存在但是 file.exists 却返回 false分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/21538.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注