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



Answers

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

File myTempDir = Files.createTempDir();

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

Question

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




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

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



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




Как обсуждалось в этом 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.
   *
   * <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)
            + ')');
  }



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

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

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

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

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




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

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



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




Links