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


Answers

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

File myTempDir = Files.createTempDir();

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

Question

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




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




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

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



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




Это то, что я решил сделать для своего собственного кода:

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



Не используйте deleteOnExit() даже если вы явно удалите его позже.

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

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

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

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




Наивно написанный код для решения этой проблемы страдает от условий гонки, включая несколько ответов здесь. Исторически вы могли бы тщательно подумать о состоянии гонки и написать ее самостоятельно, или вы могли бы использовать стороннюю библиотеку, такую ​​как 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, который попросил именно такую ​​функцию.




Для завершения это код из библиотеки 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.
   *
   * <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)
            + ')');
  }