美文网首页Java
在Windows下调用File.delete()失败的问题

在Windows下调用File.delete()失败的问题

作者: 猫和芝士蛋糕 | 来源:发表于2018-08-26 20:46 被阅读43次

    本周狗哥的QBVmAgent算是搭建的七七八八, 我和强sir拉下代码各自体验. 妹想到我这边12个单元测试竟然有两个个死活跑不过去. 而强Sir和狗哥那边都是可以正常跑过的. 黑人问号??? 想到狗哥和强Sir是MacOS, 而我的是Win10, 所以应该是由于系统导致的不一致性问题.


    IDEA下的测试截图

    这三处测试的行为都是在同一目录下生成配置文件my.conf, 如果配置文件已存在, 则需要先删除文件. 创建新配置文件的代码如下:

    private static void writeConfig(LinkedHashMap<String, String> map) {
            URL str = MyConfFileUtils.class.getClassLoader().getResource(appYmlFile);
            assert str != null;
            //去头尾
            String temp = StringUtils.remove(str.toString(), "file:");
            temp = StringUtils.remove(temp, appYmlFile);
            File conf = new File(String.format("%s%s", temp, MY_CONF));
            assert !conf.exists() || conf.delete();
    ...省略
    

    Debug进去后发现是conf.delete()返回false, 而delete调用的是本地方法, 由操作系统进行删除.

    联想到之前在Linux下开启两个SSH连接对同一个文件一边用Vim打开, 另一边用rm删除的时候, 系统并没有提示任何异常. 猜想应该是类Unix系统打开文件的同时依然可以删除文件, 而Windows在一个文件打开的同时显然是不可以删除的. 所以无法删除的原因应该是别的进/线程占用了此文件.

    由此结论进一步猜测, 是否是因为测试是并行的, 因此产生文件占用?折腾了一阵, 包括查询Junit串行跑测试的一些方法, 应用后发现问题依然存在. 而且由测试的日志时间确认, 测试本身就是串行执行的.

    测试的日志时间

    线索到这里就死掉了, 正当我蛋疼的时候, 突然发现modifyConfigTest, 这个测试对my.conf开启了一个FileInputStream, 并且没有关闭:

    FileInputStream inputStream = FileUtils.openInputStream(conf);
            Long filelength = conf.length();
            byte[] filecontent = new byte[filelength.intValue()];
            inputStream.read(filecontent);
            //缺少了inputStream.close()
    

    原来如此, 看来问题是出在对该文件操作的文件流没有正确关闭, 导致Windows平台下无法删除该文件. 添加关闭操作之后, 后面的测试全部通过.

    StackOverflow传送门 在StackOverflow上面也找到了一样的问题, 看了看回答, 跟我的猜想是一致的.

    StackOverflow回答

    根据该回答, 在Windows中, 如果当前程序打开了该文件, 并尝试删除的时候, 你需要先关闭该文件然后删除. 如果是别的程序打开了该文件, 那么你需要想办法搞清楚谁打开了此文件. 而在Linux中, 基本上没有什么可以阻止你删除文件.

    最后, 根据该回答下一些热心群众的建议, File.delete()是一个过时的方法, 在删除失败时只有一个boolean值返回. 推荐使用Files.delete(Path path)方法, 在文件被占用时操作会抛出FileSystemException异常. 而且, 上面的代码中还存在着文件路径过度依赖字符串进行处理的问题, 推荐使用Path进行路径的拼接.
    根据以上两点, 对最上文处的代码修改后, 如下所示:

    private static void writeConfig(LinkedHashMap<String, String> map) {
            URL url = MyConfFileUtils.class.getClassLoader().getResource(appYmlFile);
            assert url != null;
            try {
                Path dirPath = Paths.get(url.toURI()).getParent(); // 改为由Path处理路径
                Path currentPath = Paths.get(dirPath.toString(), MY_CONF);
                Files.deleteIfExists(currentPath); // 之前为 assert !conf.exists() || conf.delete();
    ...省略
            } catch (IOException | URISyntaxException e) {
                e.printStackTrace();
            }
        }
    

    相关文章

      网友评论

        本文标题:在Windows下调用File.delete()失败的问题

        本文链接:https://www.haomeiwen.com/subject/oytaiftx.html