Как создать временную папку / папку в Java?

Существует ли стандартный и надежный способ создания временного каталога внутри приложения Java? В базе данных проблем Java есть запись, в которой есть код кода, но мне интересно, есть ли стандартное решение, которое можно найти в одной из обычных библиотек (Apache Commons и т. Д.)?

Если вы используете JDK 7, используйте новый class Files.createTempDirectory для создания временного каталога.

Перед 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); } 

Вы можете сделать лучшие исключения (подclass IOException), если хотите.

В библиотеке Google Guava имеется множество полезных утилит. Здесь следует отметить class «Файлы» . Он содержит множество полезных методов, в том числе:

 File myTempDir = Files.createTempDir(); 

Это делает именно то, что вы просили в одной строке. Если вы прочитаете здесь документацию, вы увидите, что предлагаемая адаптация File.createTempFile("install", "dir") обычно вводит уязвимости безопасности.

Если вам нужен временный каталог для тестирования, и вы используете 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 и JavaDoc TempDirectory

Gradle:

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

Maven:

  org.junit-pioneer junit-pioneer 0.1.2 test  

Наивно написанный код для решения этой проблемы страдает от условий гонки, включая несколько ответов здесь. Исторически вы могли бы тщательно подумать о состоянии гонки и написать ее самостоятельно, или вы могли бы использовать стороннюю библиотеку, такую ​​как Guava Google (как предложил ответ Spina). Или вы можете написать багги-код.

Но с JDK 7 есть хорошие новости! Сама стандартная библиотека Java теперь обеспечивает надлежащим образом работающее (нерациональное) решение этой проблемы. Вы хотите java.nio.file.Files # createTempDirectory () . Из документации :

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

Создает новый каталог в указанном каталоге, используя префикс для генерации его имени. Полученный Путь связан с той же FileSystem, что и указанный каталог.

Информация о том, как создается имя каталога, зависит от реализации и поэтому не указана. По возможности префикс используется для создания имен кандидатов.

Это эффективно устраняет неловко древний отчет об ошибках в трекер Sun, который попросил именно такую ​​функцию.

Это исходный код для файлов библиотеки 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() даже если вы явно удалите его позже.

Google «deleteonexit is evil» для получения дополнительной информации, но суть проблемы заключается в следующем:

  1. deleteOnExit() удаляет только для нормального отключения JVM, а не deleteOnExit() или уничтожению процесса JVM.

  2. deleteOnExit() удаляет только при отключении JVM – не подходит для длительных серверных процессов, потому что:

  3. Самое злое из всех – deleteOnExit() потребляет память для каждой записи временного файла. Если ваш процесс работает в течение нескольких месяцев или создает много временных файлов за короткое время, вы потребляете память и никогда не отпускаете ее до тех пор, пока JVM не выключится.

Начиная с 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» фактически создает файл. Так почему бы просто не удалить его сначала, а затем сделать mkdir на нем?

Как обсуждалось в этом RFE и его комментариях, вы можете tempDir.delete() вызвать tempDir.delete() . Или вы можете использовать System.getProperty("java.io.tmpdir") и создать там каталог. В любом случае, вы должны помнить, что вы вызываете tempDir.deleteOnExit() , иначе файл не будет удален после завершения.

Для завершения это код из библиотеки google goava. Это не мой код, но я считаю его полезным показать здесь в этой теме.

  /** 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. * * 

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. * *

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 final String tempDir = System.getProperty("java.io.tmpdir")+"tmp"+System.nanoTime(); static { File f = new File(tempDir); if(!f.exists()) f.mkdir(); } 

И для моего приложения я решил, что добавить опцию очистки временной задержки на выходе, чтобы я добавил в крючок отключения:

 Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { //stackless deletion String root = MainWindow.tempDir; Stack dirStack = new Stack(); 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()); } } } } }); 

Метод удаляет все поддиры и файлы перед удалением темпа, не используя столбец (который является полностью необязательным, и вы можете сделать это с рекурсией на данном этапе), но я хочу быть в безопасности.

Этот код должен работать достаточно хорошо:

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

Мне нравится несколько попыток создания уникального имени, но даже это решение не исключает условия гонки. Другой процесс может проскальзывать после теста для exists() и вызова метода if(newTempDir.mkdirs()) . Я не знаю, как полностью сделать это безопасным, не прибегая к собственному коду, который, как я полагаю, является тем, что File.createTempFile() внутри File.createTempFile() .

Как вы можете видеть в других ответах, никакого стандартного подхода не возникло. Следовательно, вы уже упоминали Apache Commons, я предлагаю следующий подход с использованием FileUtils от Apache Commons IO :

 /** * 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 ()) -> Поэтому он возвращает то, что нужно большинству людей, когда они хотят создать временную директорию.

Перед Java 7 вы также можете:

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

Использование File#createTempFile и delete для создания уникального имени для каталога выглядит нормально. Вы должны добавить ShutdownHook для удаления каталога (рекурсивно) при отключении JVM.

  • Как создать файл на внутреннем хранилище Android?
  • Android-файл для сохранения на внешнем хранилище
  • Файл с измененным прослушивателем в Java
  • Проблема с Windows 7, update.packages: «невозможно переместить временную установку»?
  • .NET. Как проверить, является ли путь файлом, а не каталогом?
  • Преобразование dta-файла в csv без программного обеспечения Stata
  • Программирование на Android: с чего начать создавать простой браузер файлов?
  • Поддерживает ли HTML5 drag and drop загрузки папок или дерева папок?
  • Пересечение всех файлов в каталоге в R, применение нескольких команд
  • Java - поиск файлов в каталоге
  • Как включить файлы данных с APK приложения?
  • Давайте будем гением компьютера.