广告占位

译文:使用Java NIO2创建临时文件和目录

胡建洪   2019-02-16 阅读(92) 评论(0) 点赞(0)

摘要:临时文件和目录在许多情况下非常有用。一个典型的情况是你正在写一些(通常很大的)文件,你不希望任何人在完全写入和关闭之前不小心访问并弄乱它。一旦准备就绪,您只需将其移动到最终位置即可。 另一种情况是使用临时文件来处理大量数据,以便将RAM的使用保持在合理的限度内。这是SQL数据库在执行访问数千或数百万行的查询时所执行的操作。

临时文件和目录在许多情况下非常有用。一个典型的情况是你正在写一些(通常很大的)文件,你不希望任何人在完全写入和关闭之前不小心访问并弄乱它。一旦准备就绪,您只需将其移动到最终位置即可。

另一种情况是使用临时文件来处理大量数据,以便将RAM的使用保持在合理的限度内。这是SQL数据库在执行访问数千或数百万行的查询时所执行的操作。

此外,您还可以使用临时文件存储某些较长操作的中间结果,或者存储撤消操作或任何您想象的数据。

Java 6及之前的临时文件

如果您仍在使用Java 6或更早版本,或者您正在使用旧代码进行交互,则可以使用以下File类的静态方法:

public static File createTempFile(String prefix, String suffix) throws IOException;

此方法在依赖于系统的缺省临时文件目录(由java.io.tmpdir系统属性指定的目录)中创建空临时文件。创建的文件的名称以给定的前缀开头,以给定的后缀结束。它还包含几个随机选择的字符。前缀必须至少为3个字符,如果为第二个参数传递null,则使用默认后缀.tmp

下面是一个创建这样的临时文件并在其中写入2行的简单方法:

    private void createTempFileOldWay() throws IOException {
        File tempFile = File.createTempFile("tempfile-old", ".tmp");
        PrintWriter writer = null;
        try {
            writer = new PrintWriter(new FileWriter(tempFile));
            writer.println("Line1");
            writer.println("Line2");
        } finally {
            if (writer != null) writer.close();
        }
        System.out.printf("Wrote text to temporary file %s%n", tempFile.toString());
    }

Java 6及之前的临时目录

在这里,我们必须面对Java 6及更早版本没有提供任何方便的方法来创建临时目录的事实。因此,您需要根据java.io.tmpdir系统属性的值手动编写一个。

下面是一种创建临时文件并将其写入临时目录的示例方法。请注意,这次我们使用createTempFile()方法的版本,该方法也接受将放置临时文件的目录。

    private void createTempFileWithDirOldWay() throws IOException {
        File tempDir = new File(System.getProperty("java.io.tmpdir", null), "tempdir-old");
        if (!tempDir.exists() && !tempDir.mkdir())
            throw new IIOException("Failed to create temporary directory " + tempDir);
        File tempFile = File.createTempFile("tempfile-old", ".tmp", tempDir);
        PrintWriter writer = null;
        try {
            writer = new PrintWriter(new FileWriter(tempFile));
            writer.println("Line1");
            writer.println("Line2");
        } finally {
            if (writer != null) writer.close();
        }
        System.out.printf("Wrote text to temporary file %s%n", tempFile.toString());
    }

Java 6及更早版本中的临时文件和目录的问题

除了缺少创建临时目录的方法之外,还有一个问题。使用上述方法创建的临时文件具有非常不受限制的文件权限,因此(至少在我的系统上)任何人都可以读取它。如果您正在编写安全敏感的应用程序(例如,用于加密数据),则可能是一个严重的问题。

Java 7中的新增功能

作为NIO2的一部分,Java 7引入了几个用于管理文件的新类。两个最有趣的是路径文件。第一个与File类非常相似,但它的名称更好地描述了它的功能。第二个是一个简单的实用程序类,只有静态方法,可以对文件执行几个非常常见的操作。

在Java 7中创建临时文件

要在系统相关的默认临时目录中创建临时文件,您可以使用Files类中的以下方法:

public static Path createTempFile(String prefix, String suffix, FileAttribute<?>... attrs) throws IOException;

这看起来与以前的做法非常相似,但差异很小。前缀和后缀是可选的,可以为null。属性也是可选的,您不必传递任何属性。

从安全的角度来看,最重要的是创建的临时文件具有非常严格的权限,这对于安全敏感的应用程序应该是可接受的。

以下是典型用法:

private void createTempFile() throws IOException {
        Path tempFile = Files.createTempFile("tempfiles", ".tmp");
        List<String> lines = Arrays.asList("Line1", "Line2");
        Files.write(tempFile, lines, Charset.defaultCharset(), StandardOpenOption.WRITE);
        System.out.printf("Wrote text to temporary file %s%n", tempFile.toString());
    }

在Java 7中创建临时目录

好消息是Java 7引入了非常方便的方法来创建临时目录。完全足以调用带有指定前缀的Files类的createTempDirectory()方法(如果不关心前缀,则使用null)和可选属性。就这样。这是一个例子:

private void createTempFileWithDir() throws IOException {
        Path tempDir = Files.createTempDirectory("tempfiles");
        Path tempFile = Files.createTempFile(tempDir, "tempfiles", ".tmp");
        List<String> lines = Arrays.asList("Line1", "Line2");
        Files.write(tempFile, lines, Charset.defaultCharset(), StandardOpenOption.WRITE);
        System.out.printf("Wrote text to temporary file %s%n", tempFile.toString());
    }

删除临时文件和目录

如果执行上述方法,您将很快发现创建的临时文件不会自动删除,并且在应用程序完成后很长时间内保留在文件系统上。实际上,根据您的操作系统,文件可能会在那里停留数周甚至数月。

最好的解决方案是调用delete()方法删除不再需要的临时文件或目录,但有三种选择在某些情况下可能有用。

使用shutdown hook删除临时文件

Java运行时允许注册几个关闭挂钩,这些挂钩是应用程序正常完成后将执行的操作。您可以使用此功能并注册您自己的钩子,它将删除该文件:

    private void createTempFileShutdownHook() throws IOException {
        final Path tempFile = Files.createTempFile("tempfiles-shutdown-hook", ".tmp");
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                try {
                    Files.delete(tempFile);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        List<String> lines = Arrays.asList("Line1", "Line2");
        Files.write(tempFile, lines, Charset.defaultCharset(), StandardOpenOption.WRITE);
        System.out.printf("Wrote text to temporary file %s%n", tempFile.toString());
    }

使用delete-on-exit删除临时文件

来自Java 6的类文件提供了deleteOnExit()方法,该方法在应用程序正常完成后调度文件以进行删除。该机制与关闭钩子非常相似,但更容易使用。

如果您有临时文件的路径,您可以轻松地将其转换为文件并在其上调用deleteOnExit()

private void createTempFileDeleteOnExit() throws IOException {
        Path tempFile = Files.createTempFile("tempfiles-delete-on-exit", ".tmp");
        tempFile.toFile().deleteOnExit();
        List<String> lines = Arrays.asList("Line1", "Line2");
        Files.write(tempFile, lines, Charset.defaultCharset(), StandardOpenOption.WRITE);

在关闭时删除临时文件

最后一种方法是使用DELETE_ON_CLOSE打开选项打开文件。此选项可确保在关闭文件后,它将自动从文件系统中删除,并且将无法再访问。这是一个例子:

private void createTempFileDeleteOnClose() throws IOException {
        Path tempFile = Files.createTempFile("tempfiles-delete-on-close", ".tmp");
        List<String> lines = Arrays.asList("Line1", "Line2");
        Files.write(tempFile, lines, Charset.defaultCharset(), StandardOpenOption.WRITE, StandardOpenOption.DELETE_ON_CLOSE);
        System.out.printf("Wrote text to temporary file %s%n", tempFile.toString());
    }

shutdown hooks和delete-on-exit的问题

关闭挂钩和退出时删除可能看起来是个好主意,因为无需担心何时删除该文件。但是,在构建像服务器这样的长时间运行的应用程序时,您应该非常小心。

第一个原因是JVM必须以某种方式存储和保存有关要删除哪些文件的信息,这反过来又增加了内存使用量。如果您创建了大量临时文件,则内存使用率可能会变得非常高。

第二个是临时文件占用一些磁盘空间,如果它们没有被定期删除,它们可能会填满文件系统并阻止在此应用程序和其他应用程序中创建其他临时文件。从用户的角度来看,这会导致系统和应用程序不稳定。

DELETE_ON_CLOSE问题

打开选项DELETE_ON_CLOSE也不完美,因为如果JVM在关闭时无法删除文件,它将在退出时安排删除。这将导致与上述相同的问题。

结论

临时文件在许多应用程序中非常有用和常见,Java 7提供了非常方便的界面来操作它们。最困难的部分是正确监视临时文件的生命周期,并在不再需要时删除它们。当临时文件很大并且应用程序运行很长时间时,这一点尤为重要。

示例源码可以从Github处得到

注:原文由Robert Piasecki20142月5日发表,原文链接:https://softwarecave.org/2014/02/05/create-temporary-files-and-directories-using-java-nio2/

喜欢 (0) or 分享 (0)

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请狠狠点击下面的

  Java File Temp NIO2
广告占位