2

我们在 Java 中有一个底层方法,它应该在调用它的 close 方法时删除源文件。

private void appendFile(Path destination, Path source) {

    try (FileChannel sourceChannel = FileChannel.open(source, StandardOpenOption.READ, StandardOpenOption.DELETE_ON_CLOSE);
         FileChannel destinationChannel = FileChannel.open(destination, StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {
        destinationChannel.transferFrom(sourceChannel, destinationChannel.size(), sourceChannel.size());
    } catch (IOException ex) {
       // Do something with this exception
    }
}

现在我们对此进行功能集成测试,发现源文件没有被删除。

有人可以帮助我们吗?

4

2 回答 2

0

StandardOpenOption.DELETE_ON_CLOSE选项文档:

当此选项存在时,实现会尽最大努力在通过适当的关闭方法关闭文件时尝试删除文件。如果未调用 close 方法,则在 Java 虚拟机终止时(正常情况下,如 Java 语言规范所定义,或在可能的情况下,异常情况下),将尽最大努力尝试删除文件。

此选项主要用于仅由 Java 虚拟机的单个实例使用的工作文件。当打开由其他实体同时打开的文件时,不建议使用此选项。关于何时以及如何删除文件的许多细节是特定于实现的,因此未指定。特别是,当文件打开时被攻击者替换时,实现可能无法保证删除预期的文件。因此,安全敏感的应用程序在使用此选项时应小心。

所以这只是尽力而为,而不是100%保证会被删除。也许它仍然被其他作家打开?

于 2020-03-02T12:16:23.923 回答
0

我们在 2015 年左右遇到了类似的问题,因为 Win7/x64 上的用户经常发现程序终止后未删除的剩余文件。经过一些研究和反复试验,我们发现它只发生在最近进行内存映射的文件中,并通过避免对我们想要很快/稍后删除的文件进行内存映射来修复它。

FileChannelImpl.transferFromFileChannel内存映射传输源。(取决于您的 JVM — 我基于 OpenJDK。)虽然 JVM 通过取消映射在复制后进行清理,从而使创建的视图无效,但操作系统可能会将实际清理延迟到另一个时间点。在此之前,该文件有一个活动的(但不可访问的)内存映射,可能会阻止取消链接。

这个问题似乎相关:如何正确关闭 MappedByteBuffer?

供参考:jdk11/sun.nio.ch.FileChannelImpl#transferFromFileChannel

private long transferFromFileChannel(FileChannelImpl src,
                                     long position, long count)
    throws IOException
{
    if (!src.readable)
        throw new NonReadableChannelException();
    synchronized (src.positionLock) {
        long pos = src.position();
        long max = Math.min(count, src.size() - pos);

        long remaining = max;
        long p = pos;
        while (remaining > 0L) {
            long size = Math.min(remaining, MAPPED_TRANSFER_SIZE);
            // ## Bug: Closing this channel will not terminate the write
            MappedByteBuffer bb = src.map(MapMode.READ_ONLY, p, size);
            try {
                long n = write(bb, position);
                assert n > 0;
                p += n;
                position += n;
                remaining -= n;
            } catch (IOException ioe) {
                // Only throw exception if no bytes have been written
                if (remaining == max)
                    throw ioe;
                break;
            } finally {
                unmap(bb);
            }
        }
        long nwritten = max - remaining;
        src.position(pos + nwritten);
        return nwritten;
    }
}
于 2020-04-28T16:16:47.827 回答