How to create a temporary directory/folder in Java?

440,249

Solution 1

If you are using JDK 7 use the new Files.createTempDirectory class to create the temporary directory.

Path tempDirWithPrefix = Files.createTempDirectory(prefix);

Before JDK 7 this should do it:

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);
}

You could make better exceptions (subclass IOException) if you want.

Solution 2

The Google Guava library has a ton of helpful utilities. One of note here is the Files class. It has a bunch of useful methods including:

File myTempDir = Files.createTempDir();

This does exactly what you asked for in one line. If you read the documentation here you'll see that the proposed adaptation of File.createTempFile("install", "dir") typically introduces security vulnerabilities.

Solution 3

If you need a temporary directory for testing and you are using jUnit, @Rule together with TemporaryFolder solves your problem:

@Rule
public TemporaryFolder folder = new TemporaryFolder();

From the documentation:

The TemporaryFolder Rule allows creation of files and folders that are guaranteed to be deleted when the test method finishes (whether it passes or fails)


Update:

If you are using JUnit Jupiter (version 5.1.1 or greater), you have the option to use JUnit Pioneer which is the JUnit 5 Extension Pack.

Copied from the project documentation:

For example, the following test registers the extension for a single test method, creates and writes a file to the temporary directory and checks its content.

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

More info in the JavaDoc and the JavaDoc of TempDirectory

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>

Update 2:

The @TempDir annotation was added to the JUnit Jupiter 5.4.0 release as an experimental feature. Example copied from the JUnit 5 User Guide:

@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));
}

Solution 4

Naively written code to solve this problem suffers from race conditions, including several of the answers here. Historically you could think carefully about race conditions and write it yourself, or you could use a third-party library like Google's Guava (as Spina's answer suggested.) Or you could write buggy code.

But as of JDK 7, there is good news! The Java standard library itself now provides a properly working (non-racy) solution to this problem. You want java.nio.file.Files#createTempDirectory(). From the documentation:

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

Creates a new directory in the specified directory, using the given prefix to generate its name. The resulting Path is associated with the same FileSystem as the given directory.

The details as to how the name of the directory is constructed is implementation dependent and therefore not specified. Where possible the prefix is used to construct candidate names.

This effectively resolves the embarrassingly ancient bug report in the Sun bug tracker which asked for just such a function.

Solution 5

This is the source code to the Guava library's Files.createTempDir(). It's nowhere as complex as you might think:

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) + ')');
}

By default:

private static final int TEMP_DIR_ATTEMPTS = 10000;

See here

Share:
440,249
Peter Becker
Author by

Peter Becker

Updated on February 19, 2020

Comments

  • Peter Becker
    Peter Becker about 4 years

    Is there a standard and reliable way of creating a temporary directory inside a Java application? There's an entry in Java's issue database, which has a bit of code in the comments, but I wonder if there is a standard solution to be found in one of the usual libraries (Apache Commons etc.) ?

  • Daniel Hiller
    Daniel Hiller about 15 years
    A shutdown hook is cumbersome. Wouldn't File#deleteOnExit also work?
  • Jeremy Huiskamp
    Jeremy Huiskamp about 15 years
    What if the directory already exists and you don't have read/write access to it or what if it's a regular file? You also have a race condition there.
  • Sarel Botha
    Sarel Botha almost 15 years
    You should always check the return value for mkdir(). If that is false then it means the directory already existed. This can cause security problems, so consider whether this should raise an error in your application or not.
  • muriloq
    muriloq almost 15 years
    #deleteOnExit didn't work for me - I believe it won't delete non-empty directories.
  • Trenton
    Trenton about 14 years
    Also, deleteOnExit will not delete non-empty directories.
  • matbrgz
    matbrgz over 13 years
    Nice touch of just reusing the guaranteed temporary name.
  • matbrgz
    matbrgz over 13 years
    We have a JVM where class and jar files get hidden files underneath created by the JVM, and this extra information takes quite a while to delete. When doing hot redeploys on web containers exploding WARs, the JVM can literaly take minutes to clean up after finishing but before exiting when having run for a few hours.
  • matbrgz
    matbrgz over 13 years
    The java.io.tmpdir is shared so you need to do all the usual voodoo to avoid stepping on somebody elses toes.
  • Demiurg
    Demiurg over 13 years
    This is dangerous. Java is known not to delete files immediately, so mkdir may fail sometimes
  • TofuBeer
    TofuBeer over 13 years
    @Demiurg The only case of a file not being deleted immediately is on Windows when the file is already open (it could be open by a virus scanner for example). Do you have other documentation to show otherwise (I am curious about things like that :-)? If it happens regularly then the above code won't work, if it is rare then lopping the call to the above code until the delete happens (or some max tries is reached) would work.
  • Lenik
    Lenik over 13 years
    @Demiurg Java is known not to delete files immediately. That's true, even if you don't open it. So, a more safe way is temp.delete(); temp = new File(temp.getPath + ".d"); temp.mkdir(); ..., temp.delete();.
  • Joachim Sauer
    Joachim Sauer about 13 years
    This code has a race condition between delete() and mkdir(): A malicious process could create the target directory in the meantime (taking the name of the recently-created file). See Files.createTempDir() for an alternative.
  • abb
    abb almost 13 years
    I wonder what vulnerability you refer to. This approach does not appear to create a race condition as File.mkdir() is suppposed to fail if such the directory already exists (created by an attacker). I don't think this call will follow through a malicious symlinks either. Could you clarify what you have meant?
  • sahana
    sahana almost 13 years
    @abb: I don't know the details of the race condition that is mentioned in the Guava documentation. I suspect that the documentation is correct given that it specifically calls out the problem.
  • TofuBeer
    TofuBeer over 12 years
    I like the ! to stand out, too easy to miss it. I read a lot of code written by students... if(!i) is common enough to be annoying :-)
  • exception
    exception about 12 years
    Doesn't work in JUnit 4.8.2 on Windows 7! (Permissions issue)
  • zbyszek
    zbyszek over 11 years
    This is insecure. See comment by Joachim Sauer in the first (equally insecure) option. Proper way to check for existence of a file or dir and than to grab the filename, atomically, is by creating the file or dir.
  • zbyszek
    zbyszek over 11 years
    To add to Joachin Sauers' comment: e.g. on linux, inotify on /tmp will tell you the names of all created and deleted files. It is trivial for an attacker, a user on the same machine, to cause the process using this function to write to arbitrary file accessible by the victim.
  • Keith
    Keith over 11 years
    @zbyszek javadocs say "The UUID is generated using a cryptographically strong pseudo random number generator." Given that how does a malicious process create a dir with the same name between exists() and mkdirs(). In fact looking at this now I think my exists() test might be a little silly.
  • zbyszek
    zbyszek over 11 years
    Keith: UUID being secure or not isn't crucial in this case. It is enough for the information about the name you queried to somehow "leak". For example, let's say that the file being created is on an NFS filesystem, and the attacker can listen (passively) to packets. Or the random generator state has been leaked. In my comment I said that your solution is equally insecure as the accepted answer, but this isn't fair: the accepted one is trivial to defeat with inotify, and this one is much harder to defeat. Nevertheless, in some scenarios it is certainly possible.
  • TofuBeer
    TofuBeer over 11 years
    @zbyszek You can use the version of createTempFile that takes a File, being the temporary directory to use. Pass it a directory that only the user has access to.
  • zbyszek
    zbyszek over 11 years
    @TofuBeer: yes, with the disclaimer that "temp" is under your complete control, and that you are running only one instance of this tempdir function at a time, this function is fine. But that's not what people usually mean by "create a temporary dir".
  • TofuBeer
    TofuBeer over 11 years
    No, you misunderstand what I said (I think). Make a directory like ~/.tmp. Give it user only permissions. Then use the createTempFile method that allows you to specify the temp directory, passing in ~/.tmp. The number of times this method is called has no bearing on it (the odds of collisions are low, and can be handled via looping).
  • TofuBeer
    TofuBeer over 11 years
    Also, if you have access to JDK 7 you should use the javax.nio.Files class to create the temp directory.
  • Sarel Botha
    Sarel Botha over 11 years
    @XièJìléi That code is potentially dangerous on a Unix system because you're not checking the return of mkdir().
  • Sarel Botha
    Sarel Botha over 11 years
    @abb You're right. As long as the return of mkdir() is checked then it would be secure. The code Spina points to uses this mkdir() method. grepcode.com/file/repo1.maven.org/maven2/com.google.guava/gu‌​ava/… . This is only a potential issue on Unix systems when using the /tmp directory because it has the sticky bit enabled.
  • Adam Parkin
    Adam Parkin over 11 years
    @CraigRinger: Why is it unwise to rely on this?
  • Craig Ringer
    Craig Ringer over 11 years
    @AdamParkin Honestly, I don't remember anymore. Explanation fail!
  • sahana
    sahana over 11 years
    @SarelBotha thanks for filling in the blank here. I'd been wondering idly about this for quite some time.
  • febot
    febot almost 11 years
    Doesn't work in 4.11. getRoot() and newFile() both complained "temp dir not created yet". Besides, "Folder" is ugly Windows term. It's a directory.
  • Zoltán
    Zoltán over 10 years
    @CraigRinger java.nio.file.Files#createTempDirectory is not available before Java 7.
  • Volker Stolz
    Volker Stolz over 10 years
    See the note about the race condition in the other answer.
  • brabster
    brabster over 10 years
    I had the same thought and implemented a solution using random UUIDs bit like this one. No check for exists, just one attempt to create - strong RNG used by randomUUID method pretty much guarantees no collisions (can be used for generating primary keys in DB tables, done this myself and never known a collision), so pretty confident. If anyone's not sure, check out stackoverflow.com/questions/2513573/…
  • Martin Wickman
    Martin Wickman almost 10 years
    This I like, barring the race
  • Bogdan Calmac
    Bogdan Calmac over 8 years
    The main benefit of this approach is that the directory is managed by JUnit (created before the test and deleted recursively after the test). And it does work. If you get "temp dir not created yet", it might be because you forgot @Rule or the field in not public.
  • Adam Taras
    Adam Taras about 7 years
    Nice code. But unfortunatelly "deleteOnExit()" won't work, since Java can't delete the whole folder at once. You have to delete all files recursively :/
  • geri
    geri almost 7 years
    I implemented a quick test running with Java 8, but temp folder was not deleted, see pastebin.com/mjgG70KG
  • Erk
    Erk almost 6 years
    You could always use a UUID instead of nanoTime. UUIDs are supposed to be unique...
  • dosentmatter
    dosentmatter almost 6 years
    If you look at Java's implementation, they just generate random names until there is no collision. Their max attempts are infinite. So if somebody malicious were to keep guessing your file/directory name, it would loop forever. Here is a link to the source: hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/9fb81d7a2f16/src/sh‌​are/… I was thinking that it could somehow lock the filesystem so it could atomically generate a unique name and create the directory, but I guess it doesn't do that according to the source code.
  • Alessandro S.
    Alessandro S. over 2 years