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


当前回答

在Java 7之前,你还可以:

File folder = File.createTempFile("testFileUtils", ""); // no suffix
folder.delete();
folder.mkdirs();
folder.deleteOnExit();

其他回答

我喜欢创建唯一名称的多次尝试,但即使是这种解决方案也不排除竞争条件。另一个进程可以在exists()和if(newTempDir.mkdirs())方法调用测试之后插入。我不知道如何在不求助于本机代码的情况下完全使其安全,我认为这是隐藏在File.createTempFile()中的内容。

这是我决定为我自己的代码所做的:

/**
 * 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();
}

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

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)。

如果你需要一个临时目录进行测试,并且你正在使用jUnit, @Rule和TemporaryFolder可以解决你的问题:

@Rule
public TemporaryFolder folder = new TemporaryFolder();

从文档中可以看到:

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


更新:

如果您正在使用JUnit Jupiter(版本5.1.1或更高),您可以选择使用JUnit Pioneer,它是JUnit 5扩展包。

摘自项目文档:

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

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

更多信息在JavaDoc和TempDirectory的JavaDoc中

Gradle:

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

Maven:

<dependency>
   <groupId>org.junit-pioneer</groupId>
   <artifactId>junit-pioneer</artifactId>
   <version>0.1.2</version>
   <scope>test</scope>
</dependency>

更新2:

@TempDir注释作为实验特性被添加到JUnit Jupiter 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));
}

这段代码应该可以很好地工作:

public static File createTempDir() {
    final String baseTempPath = System.getProperty("java.io.tmpdir");

    Random rand = new Random();
    int randomInt = 1 + rand.nextInt();

    File tempDir = new File(baseTempPath + File.separator + "tempDir" + randomInt);
    if (tempDir.exists() == false) {
        tempDir.mkdir();
    }

    tempDir.deleteOnExit();

    return tempDir;
}