How to create a temporary directory/folder in Java?
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;
Peter Becker
Updated on February 19, 2020Comments
-
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 about 15 yearsA shutdown hook is cumbersome. Wouldn't File#deleteOnExit also work?
-
Jeremy Huiskamp about 15 yearsWhat 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 almost 15 yearsYou 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 almost 15 years#deleteOnExit didn't work for me - I believe it won't delete non-empty directories.
-
Trenton about 14 yearsAlso, deleteOnExit will not delete non-empty directories.
-
matbrgz over 13 yearsNice touch of just reusing the guaranteed temporary name.
-
matbrgz over 13 yearsWe 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 over 13 yearsThe java.io.tmpdir is shared so you need to do all the usual voodoo to avoid stepping on somebody elses toes.
-
Demiurg over 13 yearsThis is dangerous. Java is known not to delete files immediately, so mkdir may fail sometimes
-
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 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 about 13 yearsThis code has a race condition between
delete()
andmkdir()
: A malicious process could create the target directory in the meantime (taking the name of the recently-created file). SeeFiles.createTempDir()
for an alternative. -
abb almost 13 yearsI 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 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 over 12 yearsI 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 about 12 yearsDoesn't work in JUnit 4.8.2 on Windows 7! (Permissions issue)
-
zbyszek over 11 yearsThis 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 over 11 yearsTo 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 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 over 11 yearsKeith: 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 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 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 over 11 yearsNo, 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 over 11 yearsAlso, if you have access to JDK 7 you should use the javax.nio.Files class to create the temp directory.
-
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 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/guava/… . This is only a potential issue on Unix systems when using the /tmp directory because it has the sticky bit enabled.
-
Adam Parkin over 11 years@CraigRinger: Why is it unwise to rely on this?
-
Craig Ringer over 11 years@AdamParkin Honestly, I don't remember anymore. Explanation fail!
-
sahana over 11 years@SarelBotha thanks for filling in the blank here. I'd been wondering idly about this for quite some time.
-
febot almost 11 yearsDoesn't work in 4.11.
getRoot()
andnewFile()
both complained "temp dir not created yet". Besides, "Folder" is ugly Windows term. It's a directory. -
Zoltán over 10 years@CraigRinger
java.nio.file.Files#createTempDirectory
is not available before Java 7. -
Volker Stolz over 10 yearsSee the note about the race condition in the other answer.
-
brabster over 10 yearsI 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 almost 10 yearsThis I like, barring the race
-
Bogdan Calmac over 8 yearsThe 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 about 7 yearsNice 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 almost 7 yearsI implemented a quick test running with Java 8, but temp folder was not deleted, see pastebin.com/mjgG70KG
-
Erk almost 6 yearsYou could always use a UUID instead of nanoTime. UUIDs are supposed to be unique...
-
dosentmatter almost 6 yearsIf 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/share/… 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. over 2 yearsIt is deprecated now: guava.dev/releases/30.1-jre/api/docs/com/google/common/io/…