当前位置:  开发笔记 > 编程语言 > 正文

如何在Java中创建临时目录/文件夹?

如何解决《如何在Java中创建临时目录/文件夹?》经验,为你挑选了9个好方法。

是否有一种在Java应用程序中创建临时目录的标准且可靠的方法?Java的问题数据库中有一个条目,在评论中有一些代码,但我想知道是否有一个标准的解决方案可以在其中一个常见的库(Apache Commons等)中找到?



1> TofuBeer..:

如果您使用的是JDK 7,请使用新的Files.createTempDirectory类来创建临时目录.

Path tempDirWithPrefix = Files.createTempDirectory(prefix);

在JDK 7之前,应该这样做:

public static File createTempDirectory()
    throws IOException
{
    final File temp;

    temp = File.createTempFile("temp", Long.toString(System.nanoTime()));

    if(!(temp.delete()))
    {
        throw new IOException("Could not delete temp file: " + temp.getAbsolutePath());
    }

    if(!(temp.mkdir()))
    {
        throw new IOException("Could not create temp directory: " + temp.getAbsolutePath());
    }

    return (temp);
}

如果需要,可以创建更好的异常(子类IOException).


此代码在`delete()`和`mkdir()`之间存在竞争条件:恶意进程可以在此期间创建目标目录(获取最近创建的文件的名称).请参阅[`Files.createTempDir()`](http://guava-libraries.googlecode.com/svn/tags/release08/javadoc/com/google/common/io/Files.html#createTempDir%28%29)替代.
这很危险.众所周知,Java不会立即删除文件,因此mkdir有时可能会失败
我喜欢 !脱颖而出,太容易错过了.我读了很多学生写的代码......如果(!i)很常见,很讨厌:-)
众所周知,@ Demiurg Java不会立即删除文件.这是真的,即使你不打开它.所以,更安全的方法是`temp.delete(); temp = new File(temp.getPath +".d"); temp.mkdir(); ...,temp.delete();`.
@Demiurg当文件已经打开时(例如,它可以由病毒扫描程序打开),唯一没有立即删除文件的情况是在Windows上.你有没有其他文件显示(我很好奇这样的事情:-)?如果它经常发生,那么上面的代码将不起作用,如果很少,那么删除对上述代码的调用,直到删除发生(或达到一些最大尝试)将起作用.
添加Joachin Sauers的评论:例如在linux上,/ tmp上的inotify会告诉你所有创建和删除文件的名称.攻击者(同一台机器上的用户)使用此函数将进程写入受害者可访问的任意文件,这是微不足道的.

2> Spina..:

Google Guava库有很多实用工具.其中一个值得注意的是Files类.它有许多有用的方法,包括:

File myTempDir = Files.createTempDir();

这完全符合您在一行中提出的要求.如果你阅读这里的文档,你会发现建议的改编File.createTempFile("install", "dir")通常会引入安全漏洞.


@abb:我不知道Guava文档中提到的竞争条件的细节.我怀疑文档是正确的,因为它专门调用了问题.

3> matsev..:

如果您需要进行测试的临时目录,并且您使用JUnit,@Rule一起TemporaryFolder解决您的问题:

@Rule
public TemporaryFolder folder = new TemporaryFolder();

从文档:

TemporaryFolder规则允许创建在测试方法完成时保证删除的文件和文件夹(无论是通过还是失败)


更新:

如果您使用的是JUnit Jupiter(版本5.1.1或更高版本),则可以选择使用JUnit Pioneer,它是JUnit 5 Extension Pack.

从项目文档中复制:

例如,以下测试为单个测试方法注册扩展,创建文件并将其写入临时目录并检查其内容.

@Test
@ExtendWith(TempDirectory.class)
void test(@TempDir Path tempDir) {
    Path file = tempDir.resolve("test.txt");
    writeFile(file);
    assertExpectedFileContent(file);
}

JavaDoc和TempDirectory的JavaDoc中的更多信息

摇篮:

dependencies {
    testImplementation 'org.junit-pioneer:junit-pioneer:0.1.2'
}

Maven的:


   org.junit-pioneer
   junit-pioneer
   0.1.2
   test


更新2:

该@TempDir注释添加到JUnit的木星5.4.0版本作为实验性的功能.从JUnit 5用户指南复制的示例:

@Test
void writeItemsToFile(@TempDir Path tempDir) throws IOException {
    Path file = tempDir.resolve("test.txt");

    new ListWriter(file).write("a", "b", "c");

    assertEquals(singletonList("a,b,c"), Files.readAllLines(file));
}


自JUnit 4.7起可用
@CraigRinger:为什么依靠这个是不明智的?
@AdamParkin老实说,我不记得了.解释失败!

4> Greg Price..:

用于解决此问题的天真编写的代码会受到竞争条件的影响,包括此处的几个答案.从历史上看,您可以仔细考虑竞争条件并自行编写,或者您可以使用像Google的Guava这样的第三方库(如Spina的回答所示.)或者您可以编写有缺陷的代码.

但是从JDK 7开始,有好消息!Java标准库本身现在为这个问题提供了一个正常工作(非生动)的解决方案.你想要java.nio.file.Files #createTempDirectory().从文档:

public static Path createTempDirectory(Path dir,
                       String prefix,
                       FileAttribute... attrs)
                                throws IOException

在指定目录中创建一个新目录,使用给定的前缀生成其名称.生成的Path与给定目录的文件系统相关联.

有关如何构造目录名称的详细信息取决于实现,因此未指定.在可能的情况下,前缀用于构造候选名称.

这有效地解决了Sun bug追踪器中令人尴尬的古老bug报告,该报告要求提供这样的功能.



5> Andres Kievs..:

这是Guava库的Files.createTempDir()的源代码.它没有你想象的那么复杂:

public static File createTempDir() {
  File baseDir = new File(System.getProperty("java.io.tmpdir"));
  String baseName = System.currentTimeMillis() + "-";

  for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
    File tempDir = new File(baseDir, baseName + counter);
    if (tempDir.mkdir()) {
      return tempDir;
    }
  }
  throw new IllegalStateException("Failed to create directory within "
      + TEMP_DIR_ATTEMPTS + " attempts (tried "
      + baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
}

默认情况下:

private static final int TEMP_DIR_ATTEMPTS = 10000;

看这里



6> 小智..:

deleteOnExit()即使您稍后明确删除它也不要使用.

谷歌'deleteonexit是邪恶'获取更多信息,但问题的要点是:

    deleteOnExit() 仅删除正常JVM关闭,而不是崩溃或终止JVM进程.

    deleteOnExit() 仅在JVM关闭时删除 - 不适合长时间运行的服务器进程,因为:

    最邪恶的 - deleteOnExit()消耗每个临时文件条目的内存.如果您的进程运行了几个月,或者在短时间内创建了大量临时文件,那么您将消耗内存,并且在JVM关闭之前永远不会释放它.



7> seeker..:

从Java 1.7开始createTempDirectory(prefix, attrs),createTempDirectory(dir, prefix, attrs)包括在内java.nio.file.Files

例: File tempDir = Files.createTempDirectory("foobar").toFile();



8> Keith..:

这就是我决定为自己的代码做的事情:

/**
 * Create a new temporary directory. Use something like
 * {@link #recursiveDelete(File)} to clean this directory up since it isn't
 * deleted automatically
 * @return  the new directory
 * @throws IOException if there is an error creating the temporary directory
 */
public static File createTempDir() throws IOException
{
    final File sysTempDir = new File(System.getProperty("java.io.tmpdir"));
    File newTempDir;
    final int maxAttempts = 9;
    int attemptCount = 0;
    do
    {
        attemptCount++;
        if(attemptCount > maxAttempts)
        {
            throw new IOException(
                    "The highly improbable has occurred! Failed to " +
                    "create a unique temporary directory after " +
                    maxAttempts + " attempts.");
        }
        String dirName = UUID.randomUUID().toString();
        newTempDir = new File(sysTempDir, dirName);
    } while(newTempDir.exists());

    if(newTempDir.mkdirs())
    {
        return newTempDir;
    }
    else
    {
        throw new IOException(
                "Failed to create temp dir named " +
                newTempDir.getAbsolutePath());
    }
}

/**
 * Recursively delete file or directory
 * @param fileOrDir
 *          the file or dir to delete
 * @return
 *          true iff all files are successfully deleted
 */
public static boolean recursiveDelete(File fileOrDir)
{
    if(fileOrDir.isDirectory())
    {
        // recursively delete contents
        for(File innerFile: fileOrDir.listFiles())
        {
            if(!FileUtilities.recursiveDelete(innerFile))
            {
                return false;
            }
        }
    }

    return fileOrDir.delete();
}


这是不安全的.请参阅Joachim Sauer在第一个(同样不安全)选项中的评论.检查文件或目录是否存在以及原子地获取文件名的正确方法是创建文件或目录.
我有同样的想法,并使用像这样的随机UUID实现了一个解决方案.没有检查存在,只是一次尝试创建 - randomUUID方法使用的强RNG几乎保证没有冲突(可以用于在DB表中生成主键,自己完成并且从未知道冲突),所以非常自信.如果有人不确定,请查看http://stackoverflow.com/questions/2513573/how-good-is-javas-uuid-randomuuid

9> Paul Tomblin..:

好吧,"createTempFile"实际上创建了文件.那么为什么不先删除它,然后在上面执行mkdir呢?

推荐阅读
coco2冰冰
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有