如何在Java中创build一个临时目录/文件夹?

在Java应用程序中创build临时目录是否存在标准可靠的方法? Java的问题数据库中有一个条目,在注释中有一些代码,但是我想知道是否有一个标准的解决scheme可以在常见的库(Apache Commons等)中find?

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

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

如果你愿意,你可以做出更好的exception(子类IOException)。

谷歌番石榴图书馆有很多有用的工具。 其中一个注意事项是Files类 。 它有一堆有用的方法,包括:

 File myTempDir = Files.createTempDir(); 

这正是你所要求的一行。 如果您在这里阅读文档,您会看到File.createTempFile("install", "dir")的build议修改通常会引入安全漏洞。

如果你需要一个临时目录进行testing,而你正在使用jUnit, @RuleTemporaryFolder一起解决了你的问题:

 @Rule public TemporaryFolder folder = new TemporaryFolder(); 

从文档:

TemporaryFolder规则允许创build在testing方法结束时保证被删除的文件和文件夹(无论是否通过)

参考: http : //junit.org/apidocs/org/junit/rules/TemporaryFolder.html

天真地写代码解决这个问题遭受竞争条件,包括在这里的几个答案。 从历史上看,您可以仔细考虑竞争条件并亲自写下,或者您可以使用Google的Guava等第三方库(如Spina的答案中所build议的那样)。或者您可以编写错误的代码。

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

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

在指定的目录中创build一个新的目录,使用给定的前缀来生成它的名字。 生成的Path与给定目录相关联的文件系统相同。

关于如何构build目录名称的细节是依赖于实现的,因此没有指定。 在可能的情况下,前缀用于构build候选名称。

这有效地解决了Sun Bug跟踪器中那个仅仅需要这样一个function的古老的令人尴尬的bug报告 。

这是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; 

看这里

即使您以后明确地删除,也不要使用deleteOnExit()

谷歌“deleteonexit是邪恶的”更多的信息,但问题的要点是:

  1. deleteOnExit()只会删除正常的JVMclosures,而不会崩溃或终止JVM进程。

  2. deleteOnExit()仅在JVMclosures时删除 – 对于长时间运行的服务器进程deleteOnExit()因为:

  3. 全部最邪恶的 – deleteOnExit()消耗每个临时文件条目的内存。 如果您的进程运行了几个月,或者在短时间内创build了大量临时文件,则会消耗内存,并且在JVMclosures之前永远不会释放它。

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

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

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

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

那么,“createTempFile”实际上是创build文件。 那么为什么不先删除它,然后做mkdir呢?

正如本RFE及其评论中所讨论的,您可以先调用tempDir.delete() 。 或者你可以使用System.getProperty("java.io.tmpdir")并在那里创build一个目录。 无论哪种方式,你应该记得调用tempDir.deleteOnExit() ,否则该文件将不会被删除完成后。

我遇到了同样的问题,所以这对于那些有兴趣的人来说只是另一个答案,而且和上面的类似:

 public static final String tempDir = System.getProperty("java.io.tmpdir")+"tmp"+System.nanoTime(); static { File f = new File(tempDir); if(!f.exists()) f.mkdir(); } 

而对于我的应用程序,我决定添加一个选项来清除退出时的温度 ,所以我添加了一个closures钩子:

 Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { //stackless deletion String root = MainWindow.tempDir; Stack<String> dirStack = new Stack<String>(); dirStack.push(root); while(!dirStack.empty()) { String dir = dirStack.pop(); File f = new File(dir); if(f.listFiles().length==0) f.delete(); else { dirStack.push(dir); for(File ff: f.listFiles()) { if(ff.isFile()) ff.delete(); else if(ff.isDirectory()) dirStack.push(ff.getPath()); } } } } }); 

在删除temp之前删除所有的子和文件,而不使用callstack(这是完全可选的,你可以在这一点上recursion),但我想保持安全。

只是为了完成,这是从谷歌guava库的代码。 这不是我的代码,但我认为在这个线程中显示它是有价值的。

  /** Maximum loop count when creating temp directories. */ private static final int TEMP_DIR_ATTEMPTS = 10000; /** * Atomically creates a new directory somewhere beneath the system's temporary directory (as * defined by the {@code java.io.tmpdir} system property), and returns its name. * * <p>Use this method instead of {@link File#createTempFile(String, String)} when you wish to * create a directory, not a regular file. A common pitfall is to call {@code createTempFile}, * delete the file and create a directory in its place, but this leads a race condition which can * be exploited to create security vulnerabilities, especially when executable files are to be * written into the directory. * * <p>This method assumes that the temporary volume is writable, has free inodes and free blocks, * and that it will not be called thousands of times per second. * * @return the newly-created directory * @throws IllegalStateException if the directory could not be created */ 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) + ')'); } 

这个代码应该工作得很好:

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

我喜欢多次尝试创build一个独特的名字,但即使这个解决scheme也不排除竞争条件。 在对exists()if(newTempDir.mkdirs())方法的调用进行testing之后,另一个进程可能会进入。 我不知道如何完全做到这一点,而不诉诸于本地代码,我认为是埋在File.createTempFile()

正如你在其他答案中看到的,没有标准的方法出现。 因此,您已经提到了Apache Commons,我提出了使用Apache Commons IO中的 FileUtils的以下方法:

 /** * Creates a temporary subdirectory in the standard temporary directory. * This will be automatically deleted upon exit. * * @param prefix * the prefix used to create the directory, completed by a * current timestamp. Use for instance your application's name * @return the directory */ public static File createTempDirectory(String prefix) { final File tmp = new File(FileUtils.getTempDirectory().getAbsolutePath() + "/" + prefix + System.currentTimeMillis()); tmp.mkdir(); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { try { FileUtils.deleteDirectory(tmp); } catch (IOException e) { e.printStackTrace(); } } }); return tmp; } 

这是首选,因为apache常见的库最接近问题的“标准”,并与JDK 7和旧版本兼容。 这也返回一个“旧”的文件实例(这是基于stream),而不是一个“新”的path实例(这是基于缓冲区,将是JDK7的getTemporaryDirectory()方法的结果) – >因此,它返回大多数人需要什么时他们想创build一个临时目录。

在Java 7之前,您还可以:

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

使用File#createTempFiledelete来为目录创build一个唯一的名字似乎没问题。 您应该添加ShutdownHook以在JVMclosures时删除目录(recursion)。