Files.newOutputStream(path, CREATE_NEW, DELETE_ON_CLOSE) 不写入文件

Max*_*Max 4 java nio

我有以下代码:

import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;

import static java.nio.file.StandardOpenOption.CREATE_NEW;
import static java.nio.file.StandardOpenOption.DELETE_ON_CLOSE;

public class Test {
    public static void main(String[] args) throws IOException {
        try (OutputStream outputStream = Files.newOutputStream(Paths.get("test"), CREATE_NEW, DELETE_ON_CLOSE)) {
            outputStream.write(123);
            outputStream.flush();
            System.out.println("done");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我在调用上设置了一个断点System.out.println并检查了我的工作目录。没有名为test. 为什么输出流不写入文件?

Sub*_*mal 7

原因是在 Linux 上,即使文件打开,您也可以从目录中删除文件(此处静默假定适当的权限)。在 Windows 下这是不可能的。

来自sun.nio.fs.UnixChannelFactory的源码

// 如果在关闭时删除,则立即取消链接文件。规范很清楚
// 当
打开后被攻击者替换时,实现不能保证取消链接正确的文件。
如果(flags.deleteOnClose){ ...

如果您将代码修改为

for (int i = 0; i < 10; i++) {
    outputStream.write(123);
    outputStream.flush();
    System.out.println("flush...");
    Thread.sleep(10_000);
}
Run Code Online (Sandbox Code Playgroud)

您可以看到该文件已打开但已被删除

# assumed that the code write to Paths.get("/tmp/test")
lsof | grep "/tmp/test"
...  /tmp/test (deleted)
Run Code Online (Sandbox Code Playgroud)

编辑如果您只想确保在应用程序退出时删除临时文件,请查看下面的代码段。

import java.io.File;
import java.io.OutputStream;
import java.nio.file.Files;
import static java.nio.file.StandardOpenOption.CREATE_NEW;
public class Main {
    public static void main(String[] args) throws Exception {
        File file = new File("/tmp/test");
        file.deleteOnExit();
        System.out.println("tempFile = " + tempFile);
        try (OutputStream outputStream = Files.newOutputStream(file.toPath(),
                CREATE_NEW)) {
            outputStream.write(123);
            outputStream.flush();
            System.out.println("done");
        }
        System.out.printf("%s exists: %s%n", file, file.exists());
    }
}
Run Code Online (Sandbox Code Playgroud)

该文件/tmp/test将在应用程序完成时删除。

输出(此时文件还存在)

/tmp/test exists: true
Run Code Online (Sandbox Code Playgroud)

如果你现在检查控制台

$ ls /tmp/test
ls: cannot access '/tmp/test': No such file or directory
Run Code Online (Sandbox Code Playgroud)

如果您甚至不关心文件名,您可能会考虑使用随机生成的文件名。

File tempFile = File.createTempFile("foo_", ".tmp", new File("/tmp"));
Run Code Online (Sandbox Code Playgroud)

编辑另一种解决方案可能是。

  1. 创建文件(最好使用随机临时文件名)
  2. 打开一个 InputStream
  3. 打开OutputStreamDELETE_ON_CLOSE

按照这个顺序做它会像你期望的那样工作。

在下面找到一个工作片段。

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardOpenOption.APPEND;
import static java.nio.file.StandardOpenOption.DELETE_ON_CLOSE;
import static java.nio.file.StandardOpenOption.READ;

public class DeleteOnClose {

    public static void main(String[] args) throws IOException {
        Path path = Paths.get("/tmp/test");
        System.out.println("before create: " + Files.exists(path));
        Files.createFile(path);
        System.out.println("after create: " + Files.exists(path));
        try (InputStream in = Files.newInputStream(path, READ);
                OutputStream out = Files.newOutputStream(path, APPEND, 
                        DELETE_ON_CLOSE)) {
            out.write("Hello file!".getBytes(UTF_8));
            out.flush();

            for (int c = in.read(); c >= 0; c = in.read()) {
                System.out.print((char) c);
            }
            System.out.println();
        }
        System.out.println("after close: " + Files.exists(path));
    }
}
Run Code Online (Sandbox Code Playgroud)

输出

before create: false
after create: true
Hello file!
after close: false
Run Code Online (Sandbox Code Playgroud)