不同平台下StandardOpenOption.DELETE_ON_CLOSE的行为不一致问题咨询
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—likeFiles.size(path)orpath.toFile().length()—will throw aNoSuchFileException, 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_CLOSEmarks 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

