Java에서 임시 디렉토리 / 폴더를 만드는 방법은 무엇입니까?



Answers

Google Guava 라이브러리에는 유용한 유틸리티가 많이 있습니다. 여기서 주목해야 할 것은 Files 클래스 입니다. 그것은 다음과 같은 유용한 방법들을 가지고 있습니다 :

File myTempDir = Files.createTempDir();

이것은 당신이 한 줄에서 물어 본 것과 정확히 같습니다. 이 문서를 읽으면 File.createTempFile("install", "dir") 의 제안 된 수정이 일반적으로 보안 취약점을 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();
}



고유 한 이름을 만들려는 시도가 마음에 들지만이 솔루션조차도 경쟁 조건을 배제하지 않습니다. exists()if(newTempDir.mkdirs()) 메소드 호출에 대한 테스트가 끝나면 다른 프로세스가 if(newTempDir.mkdirs()) 있습니다. File.createTempFile() 안에 묻혀있는 원시 코드를 사용하지 않고이 코드를 완전히 안전하게 만드는 방법을 모르겠습니다.




나중에 명시 적으로 삭제하더라도 deleteOnExit() 사용하지 마십시오.

Google 'deleteonexit is evil'에 대한 자세한 정보는 문제의 요점은 다음과 같습니다.

  1. deleteOnExit() 은 일반적인 JVM 종료 만 삭제하고 JVM 프로세스가 손상되거나 종료되지 않습니다.

  2. deleteOnExit() 은 JVM 종료시 삭제 deleteOnExit() 기 때문에 다음과 같은 이유로 장시간 실행되는 서버 프로세스에는 적합하지 않습니다.

  3. 가장 나쁜 것 - deleteOnExit() 는 각 임시 파일 항목에 대해 메모리를 사용합니다. 프로세스가 몇 달 동안 실행 중이거나 짧은 시간 내에 많은 임시 파일을 생성하는 경우 메모리를 소비하고 JVM이 종료 될 때까지 메모리를 해제하지 마십시오.




Java 7 이전에는 다음 작업도 수행 할 수있었습니다.

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



이 문제를 해결하기위한 순진한 코드는 여러 가지 답변을 포함하여 경쟁 조건으로 인해 어려움을 겪습니다. 역사적으로 경쟁 조건을주의 깊게 생각하고 직접 작성하거나 Spina의 대답처럼 Google의 Guava와 같은 제 3 자 라이브러리를 사용할 수도 있습니다. 또는 버그가있는 코드를 작성할 수도 있습니다.

그러나 JDK 7부터 좋은 소식이 있습니다! Java 표준 라이브러리 자체가 이제는이 문제에 대한 제대로 작동하는 (비 반응적인) 솔루션을 제공합니다. java.nio.file.Files # createTempDirectory ()를 원한다. 문서에서 :

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

이름을 생성하기 위해서 지정된 접두사를 사용해, 지정된 디렉토리에 새로운 디렉토리를 작성합니다. 결과 Path는 주어진 디렉토리와 동일한 FileSystem과 연관됩니다.

디렉토리의 이름이 어떻게 구성되는지에 대한 세부 사항은 구현에 따라 다르므로 지정되지 않습니다. 가능한 경우 접두사는 후보 이름을 구성하는 데 사용됩니다.

이것은 Sun 버그 추적기에있는 당황스러운 고대 버그 보고서 를 효과적으로 해결하여 그러한 기능을 요구했습니다.




이 RFE 및 주석에서 설명한 것처럼 tempDir.delete() 먼저 호출 할 수 있습니다. 또는 System.getProperty("java.io.tmpdir") 를 사용하여 거기에 디렉토리를 만들 수 있습니다. 어느 쪽이든, tempDir.deleteOnExit() 을 호출하는 것을 기억해야합니다. 그렇지 않으면 완료 후에 파일이 삭제되지 않습니다.




완료만으로도 google 구아바 라이브러리의 코드입니다. 그것은 내 코드가 아니지만이 스레드에 여기에 표시하는 것이 중요하다고 생각합니다.

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



Related