403

Java アプリケーション内に一時ディレクトリを作成する標準的で信頼できる方法はありますか? Javaの問題データベースにエントリがあり、コメントにコードが少しありますが、通常のライブラリ(Apache Commonsなど)の1つに標準的な解決策があるのでしょうか?

4

18 に答える 18

425

JDK 7 を使用している場合は、新しいFiles.createTempDirectoryクラスを使用して一時ディレクトリを作成します。

Path tempDirWithPrefix = Files.createTempDirectory(prefix);

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

必要に応じて、より良い例外 (サブクラス IOException) を作成できます。

于 2009-03-06T01:31:27.817 に答える
183

Google Guava ライブラリには、便利なユーティリティがたくさんあります。ここで注目すべきはFiles クラスです。次のような便利なメソッドがたくさんあります。

File myTempDir = Files.createTempDir();

これは、1行で要求したことを正確に実行します。こちらのドキュメントを読むと、提案された の適応がFile.createTempFile("install", "dir")通常、セキュリティの脆弱性をもたらすことがわかります。

于 2011-06-19T17:15:17.127 に答える
177

テスト用の一時ディレクトリが必要で、jUnit を使用している場合は、次の方法で問題@RuleTemporaryFolder解決できます。

@Rule
public TemporaryFolder folder = new TemporaryFolder();

ドキュメントから:

TemporaryFolder ルールを使用すると、テスト メソッドが終了したときに (合格または不合格にかかわらず) 削除されることが保証されているファイルとフォルダーを作成できます。


アップデート:

JUnit Jupiter (バージョン 5.1.1 以降) を使用している場合は、JUnit 5 拡張パックである JUnit Pioneer を使用するオプションがあります。

プロジェクトドキュメントからコピー:

たとえば、次のテストでは、単一のテスト メソッドの拡張子を登録し、ファイルを作成して一時ディレクトリに書き込み、その内容をチェックします。

@Test
@ExtendWith(TempDirectory.class)
void test(@TempDir Path tempDir) {
    Path file = tempDir.resolve("test.txt");
    writeFile(file);
    assertExpectedFileContent(file);
}

詳細については、JavaDocおよびTempDirectoryの JavaDoc を参照してください。

グレード:

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

メイヴン:

<dependency>
   <groupId>org.junit-pioneer</groupId>
   <artifactId>junit-pioneer</artifactId>
   <version>0.1.2</version>
   <scope>test</scope>
</dependency>

更新 2:

@TempDirアノテーションは、実験的な機能として JUnit Jupiter 5.4.0 リリースに追加されましたJUnit 5 ユーザーガイドからコピーした例:

@Test
void writeItemsToFile(@TempDir Path tempDir) throws IOException {
    Path file = tempDir.resolve("test.txt");

    new ListWriter(file).write("a", "b", "c");

    assertEquals(singletonList("a,b,c"), Files.readAllLines(file));
}
于 2011-05-31T09:10:49.917 に答える
43

この問題を解決するために素朴に書かれたコードは、ここでの回答のいくつかを含め、競合状態に悩まされます。歴史的には、競合状態について慎重に考えて自分で書くか、Google の Guava のようなサードパーティのライブラリを使用することができました (Spina の回答が示唆したように)。または、バグのあるコードを書くことができました。

しかし、JDK 7 の時点で、朗報があります。Java 標準ライブラリ自体が、この問題に対して適切に機能する (際どくない) ソリューションを提供するようになりました。java.nio.file.Files#createTempDirectory()が必要です。ドキュメントから:

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

指定されたプレフィックスを使用して名前を生成し、指定されたディレクトリに新しいディレクトリを作成します。結果の Path は、指定されたディレクトリと同じ FileSystem に関連付けられます。

ディレクトリの名前がどのように構築されるかに関する詳細は、実装に依存するため、指定されていません。可能であれば、プレフィックスを使用して候補名を作成します。

これは、まさにそのような機能を求めていた、Sun のバグ トラッカーの恥ずかしいほど古いバグ レポートを効果的に解決します。

于 2011-10-18T07:21:52.683 に答える
38

これは 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;

こちらをご覧ください

于 2012-01-25T06:58:52.853 に答える
20

Java 1.7 の時点でcreateTempDirectory(prefix, attrs)createTempDirectory(dir, prefix, attrs)に含まれていますjava.nio.file.Files

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

于 2012-01-09T20:33:18.900 に答える
5

さて、「createTempFile」は実際にファイルを作成します。それでは、最初に削除してから mkdir を実行してみませんか?

于 2008-12-17T20:33:03.067 に答える
4

このコードはかなりうまく機能するはずです:

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;
}
于 2008-12-17T20:55:24.833 に答える
3

この RFEとそのコメントで説明されているように、最初に電話することができますtempDir.delete()。またはSystem.getProperty("java.io.tmpdir")、そこにディレクトリを使用して作成することもできます。いずれにせよ、忘れずに を呼び出す必要がtempDir.deleteOnExit()あります。そうしないと、終了後にファイルが削除されません。

于 2008-12-17T20:33:56.297 に答える
2

私は同じ問題を抱えていたので、これは興味のある人のための別の答えであり、上記のいずれかに似ています:

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<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を削除する前にすべてのサブディレクトリとファイルを削除しますが、コールスタックを使用せずに (これは完全にオプションであり、この時点で再帰を使用して行うことができます)、安全を確保したいと考えています。

于 2013-01-29T01:15:47.310 に答える
2

この小さな例を試してください:

コード:

try {
    Path tmpDir = Files.createTempDirectory("tmpDir");
    System.out.println(tmpDir.toString());
    Files.delete(tmpDir);
} catch (IOException e) {
    e.printStackTrace();
}


インポート:
java.io.IOException
java.nio.file.Files
java.nio.file.Path

Windows マシンのコンソール出力:
C:\Users\userName\AppData\Local\Temp\tmpDir2908538301081367877

コメント: Files.createTempDirectory
は一意の ID をアトマティックに生成します - 2908538301081367877



于 2020-02-13T11:10:51.433 に答える
1

一意の名前を作成するための複数の試行が気に入っていますが、この解決策でも競合状態を排除することはできません. exists()テストとif(newTempDir.mkdirs())メソッド呼び出しの後に、別のプロセスが入り込む可能性があります。ネイティブ コードに頼らずにこれを完全に安全にする方法がわかりませんFile.createTempFile()

于 2011-01-12T19:04:30.487 に答える
1

Java 7 より前は、次のこともできました。

File folder = File.createTempFile("testFileUtils", ""); // no suffix
folder.delete();
folder.mkdirs();
folder.deleteOnExit();
于 2017-05-15T08:56:48.713 に答える
0

File#createTempFileとを使用deleteしてディレクトリに一意の名前を作成しても問題ないようです。ShutdownHookJVM のシャットダウン時に (再帰的に) ディレクトリを削除するには、 を追加する必要があります。

于 2009-03-06T09:55:44.017 に答える