You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不同平台下StandardOpenOption.DELETE_ON_CLOSE的行为不一致问题咨询

Why StandardOpenOption.DELETE_ON_CLOSE behaves differently on Windows vs Linux

Root Cause

The discrepancy boils down to fundamental differences in how Windows and Unix-like systems (such as Linux) handle file deletion and resource management:

  • Linux/Unix: When you open a file with DELETE_ON_CLOSE, the OS immediately removes the file's directory entry (unlinks it) from the filesystem. However, the actual file data stays on disk until all open handles to it are closed. Since the directory entry no longer exists, any subsequent attempts to access the file via its original path—like Files.size(path) or path.toFile().length()—will throw a NoSuchFileException, even though the file is still open and readable via the input stream.

  • Windows: The OS uses a file-locking approach here. Setting DELETE_ON_CLOSE marks the file for deletion, but it doesn't remove the directory entry or make the path inaccessible until the last handle to the file is closed. While the stream is open, you can still access the file via its path to retrieve attributes like size without issues.

The Javadoc for DELETE_ON_CLOSE explicitly states it's a "best effort attempt" to delete the file on close, which gives room for platform-specific implementations tailored to the underlying filesystem's capabilities.

Solutions

1. Access file attributes via the open stream/channel instead of the path

Instead of querying the file path for attributes after opening with DELETE_ON_CLOSE, retrieve details directly from the open input stream's associated channel. This works across both platforms because you're interacting with the open file handle, not the now-invalid path on Linux.

Modified code snippet:

import java.io.*;
import java.nio.file.*;
import java.util.Arrays;
import java.nio.channels.FileChannel;

public class Main {
    public static void main(String[] args) throws IOException {
        Path path = Files.createTempFile(null, null);
        byte[] array = "test".getBytes();
        Files.write(path, array);
        System.err.println("1 File.length = " + path.toFile().length());
        System.err.println("1 Files.size = " + Files.size(path));
        
        try (InputStream in = Files.newInputStream(path, StandardOpenOption.DELETE_ON_CLOSE);
             FileChannel channel = ((FileInputStream) in).getChannel()) {
            // Get size from the open channel instead of the path
            System.err.println("2 Channel.size = " + channel.size());
            
            byte[] buf = new byte[array.length];
            in.read(buf, 0, array.length);
            if (Arrays.equals(array, buf)) {
                System.err.println("Read OK");
            }
        } finally {
            // On Linux, this will likely return false since the path was already unlinked
            if (Files.deleteIfExists(path)) {
                System.err.println("Deleted");
            }
        }
    }
}

2. Retrieve all path-dependent attributes before opening the file with DELETE_ON_CLOSE

If you need attributes like file size, fetch them before opening the stream with DELETE_ON_CLOSE. Once the stream is open, avoid any operations that rely on the file path.

Example adjustment:

public static void main(String[] args) throws IOException {
    Path path = Files.createTempFile(null, null);
    byte[] array = "test".getBytes();
    Files.write(path, array);
    
    // Fetch all needed attributes BEFORE opening with DELETE_ON_CLOSE
    long fileSize = Files.size(path);
    System.err.println("1 File.length = " + path.toFile().length());
    System.err.println("1 Files.size = " + fileSize);
    
    try (InputStream in = Files.newInputStream(path, StandardOpenOption.DELETE_ON_CLOSE)) {
        // Use the pre-fetched size instead of querying via path
        System.err.println("2 File.size (pre-fetched) = " + fileSize);
        
        byte[] buf = new byte[array.length];
        in.read(buf, 0, array.length);
        if (Arrays.equals(array, buf)) {
            System.err.println("Read OK");
        }
    } finally {
        Files.deleteIfExists(path);
    }
}

3. Avoid DELETE_ON_CLOSE and manually delete the file

If cross-platform consistency for path access is critical, skip DELETE_ON_CLOSE entirely and delete the file explicitly in a finally block. This ensures the path remains valid until you intentionally remove it:

try (InputStream in = Files.newInputStream(path)) {
    // All path operations work normally here
    System.err.println("2 File.length = " + path.toFile().length());
    System.err.println("2 Files.size = " + Files.size(path));
    
    // Read operation...
    byte[] buf = new byte[array.length];
    in.read(buf, 0, array.length);
    if (Arrays.equals(array, buf)) {
        System.err.println("Read OK");
    }
} finally {
    // Explicit deletion
    if (Files.deleteIfExists(path)) {
        System.err.println("Deleted");
    }
}

Key Takeaway

Always keep in mind that DELETE_ON_CLOSE's behavior is platform-dependent due to core filesystem differences. To write robust cross-platform code, avoid relying on the file path after opening the file with this option, and instead use the open stream/channel for any file attribute queries.

内容的提问来源于stack exchange,提问作者Robert Hume

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 12:37:45