Windowsで一時ディレクトリを作成しますか?


126

Windowsで一時ディレクトリ名を取得する最良の方法は何ですか?を使用GetTempPathGetTempFileNameて一時ファイルを作成できることがわかりmkdtempましたが、一時ディレクトリを作成するためのLinux / BSD 機能に相当するものはありますか?


この質問を見つけるのは少し難しいようです。特に、スタックオーバーフローの検索ボックスに「temp directory .net」のように入力した場合は表示されません。答えはこれまでのところすべての.NETの答えであるため、それは残念なことのようです。「.net」タグを追加できると思いますか?(そして、おそらく「ディレクトリ」または「一時ディレクトリ」タグですか?)または、タイトルに「.NET」という単語を追加しますか?たぶん、質問の本文で「temp」と「temporary」を交互に言っているかもしれません-したがって、より短い形式を検索する場合でも、テキスト検索で十分に一致します。私にはこれらのことを自分で行うのに十分な担当者がいないようです。ありがとう。
クリス

さて、私は.NET以外の回答を探していたので、それは省略したいのですが、提案された他の編集を行いました。ありがとう。
ジョシュケリー

@Josh Kelley:Win32 APIを再確認したところ、一時パスを取得し、ramdomファイル名を生成し、ディレクトリを作成するという同様のアプローチに従う唯一のオプションがありました。
スコットドーマン

回答:


230

いいえ、mkdtempに相当するものはありません。最良のオプションは、GetTempPathGetRandomFileNameの組み合わせを使用することです。

次のようなコードが必要になります。

public string GetTemporaryDirectory()
{
   string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
   Directory.CreateDirectory(tempDirectory);
   return tempDirectory;
}

3
これは少し危険なようです。特に、Path.Combine(Path.GetTempPath()、Path.GetRandomFileName())が既に存在するディレクトリの名前を返す可能性があります(小さいですが、ゼロではありませんか?)。tempDirectoryがすでに存在する場合、Directory.CreateDirectory(tempDirectory)は例外をスローしないため、このケースはアプリケーションによって検出されません。そして、2つのアプリケーションがお互いの作業を踏んでいる場合があります。.NETに安全な代替策はありますか?
クリス

22
@Chris:GetRandomFileNameメソッドは、フォルダー名またはファイル名のいずれかとして使用できる暗号的に強力なランダムな文字列を返します。結果として生じるパスがすでに存在している可能性があることは理論的には可能だと思いますが、これを行う他の方法はありません。パスが存在するかどうかを確認し、パスが再度Path.GetRandomFileName()を呼び出すかどうかを確認して、繰り返します。
スコットドーマン

5
@Chris:はい、その程度のセキュリティが心配な場合、別のプロセスがPath.CombineとDirectory.CreateDirectory呼び出しの間にディレクトリを作成する可能性は非常にわずかです。
スコットドーマン

8
GetRandomFileNameは、11個のランダムな小文字と数字を生成します。これは、ドメインサイズが(26 + 10)^ 11 =〜57ビットであることを意味します。あなたはいつでもそれを二乗するために2つの呼び出しをすることができます
Marty Neal 2013

27
ディレクトリが存在しないことを確認した直後に、godは宇宙を一時停止し、侵入しようとしているすべての同じファイルを含む同じディレクトリを作成し、アプリを爆発させて、1日を台無しにしてしまう可能性があります。
Triynko 2016

25

私はハックPath.GetTempFileName()して、ディスク上に有効な疑似ランダムファイルパスを与えてから、ファイルを削除し、同じファイルパスでディレクトリを作成します。

これにより、スコットドーマンの回答に対するクリスのコメントに従って、ファイルパスがしばらくの間またはループで使用可能かどうかを確認する必要がなくなります。

public string GetTemporaryDirectory()
{
  string tempFolder = Path.GetTempFileName();
  File.Delete(tempFolder);
  Directory.CreateDirectory(tempFolder);

  return tempFolder;
}

暗号で保護されたランダムな名前が本当に必要な場合は、スコットの回答を調整して、whileまたはdoループを使用して、ディスク上にパスを作成し続けることができます。


7

CoCreateGuid()のようなGUID作成関数であるGetTempPath()、およびCreateDirectory()を使用したい。

GUIDは一意性の高い確率を持つように設計されており、誰かがGUIDと同じ形式でディレクトリを手動で作成することはほとんどありません(作成した場合、CreateDirectory()はその存在を示して失敗します)。


5

@クリス。私も一時的なディレクトリがすでに存在する可能性があるというリモートのリスクに取りつかれていました。ランダムで暗号的に強力なものについての議論は、私を完全に満足させるものでもありません。

私のアプローチは、O / Sがファイルを作成するための2つの呼び出しを両方とも成功させてはならないという基本的な事実に基づいています。.NET設計者がディレクトリのWin32 API機能を非表示にすることを選択したのは少し意外です。これは、2回目のディレクトリの作成を試みたときにエラーを返すため、これをはるかに簡単にします。これが私が使うものです:

    [DllImport(@"kernel32.dll", EntryPoint = "CreateDirectory", SetLastError = true, CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool CreateDirectoryApi
        ([MarshalAs(UnmanagedType.LPTStr)] string lpPathName, IntPtr lpSecurityAttributes);

    /// <summary>
    /// Creates the directory if it does not exist.
    /// </summary>
    /// <param name="directoryPath">The directory path.</param>
    /// <returns>Returns false if directory already exists. Exceptions for any other errors</returns>
    /// <exception cref="System.ComponentModel.Win32Exception"></exception>
    internal static bool CreateDirectoryIfItDoesNotExist([NotNull] string directoryPath)
    {
        if (directoryPath == null) throw new ArgumentNullException("directoryPath");

        // First ensure parent exists, since the WIN Api does not
        CreateParentFolder(directoryPath);

        if (!CreateDirectoryApi(directoryPath, lpSecurityAttributes: IntPtr.Zero))
        {
            Win32Exception lastException = new Win32Exception();

            const int ERROR_ALREADY_EXISTS = 183;
            if (lastException.NativeErrorCode == ERROR_ALREADY_EXISTS) return false;

            throw new System.IO.IOException(
                "An exception occurred while creating directory'" + directoryPath + "'".NewLine() + lastException);
        }

        return true;
    }

管理されていないp / invokeコードの「コスト/リスク」がそれに値するかどうかを判断できます。ほとんどはそうではないと言うでしょうが、少なくともあなたは今、選択肢があります。

CreateParentFolder()は、演習として学生に残されています。Directory.CreateDirectory()を使用しています。ルートではnullであるため、ディレクトリの親を取得するときは注意してください。


5

私は通常これを使用します:

    /// <summary>
    /// Creates the unique temporary directory.
    /// </summary>
    /// <returns>
    /// Directory path.
    /// </returns>
    public string CreateUniqueTempDirectory()
    {
        var uniqueTempDir = Path.GetFullPath(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));
        Directory.CreateDirectory(uniqueTempDir);
        return uniqueTempDir;
    }

このディレクトリ名が一時パスに存在しないことを確実にしたい場合は、この一意のディレクトリ名が存在するかどうかを確認し、実際に存在する場合は別のディレクトリ名を作成する必要があります。

しかし、このGUIDベースの実装で十分です。この場合問題はありません。一部のMSアプリケーションは、GUIDベースの一時ディレクトリも使用します。


1

GetTempPathは正しい方法です。この方法についてのあなたの懸念が何であるかわかりません。その後、CreateDirectoryを使用して作成できます。


1つの問題は、GetTempFileNameが0バイトのファイルを作成することです。代わりに、GetTempPath、GetRandomFileName、およびCreateDirectoryを使用する必要があります。
スコットドーマン

どちらも問題なく、可能で、実行可能です。私はコードを提供するつもりでしたが、ドーマンは私の前にそれを手に入れました、そしてそれは正しく動作します。

1

ここでは、一時ディレクトリ名の衝突問題を解決するためのやや強引なアプローチを示します。これは完全なアプローチではありませんが、フォルダーパスの衝突の可能性を大幅に削減します。

一時的なディレクトリ名でこのような情報を表示することは望ましくないかもしれませんが、他のプロセスやアセンブリ関連の情報をディレクトリ名に追加して、衝突の可能性をさらに減らすことができます。また、時間に関連するフィールドが組み合わされる順序を混合して、フォルダー名をよりランダムに見えるようにすることもできます。私は個人的には、デバッグ中にそれらすべてを見つけるのが簡単なので、そのままにしておくことを好みます。

string randomlyGeneratedFolderNamePart = Path.GetFileNameWithoutExtension(Path.GetRandomFileName());

string timeRelatedFolderNamePart = DateTime.Now.Year.ToString()
                                 + DateTime.Now.Month.ToString()
                                 + DateTime.Now.Day.ToString()
                                 + DateTime.Now.Hour.ToString()
                                 + DateTime.Now.Minute.ToString()
                                 + DateTime.Now.Second.ToString()
                                 + DateTime.Now.Millisecond.ToString();

string processRelatedFolderNamePart = System.Diagnostics.Process.GetCurrentProcess().Id.ToString();

string temporaryDirectoryName = Path.Combine( Path.GetTempPath()
                                            , timeRelatedFolderNamePart 
                                            + processRelatedFolderNamePart 
                                            + randomlyGeneratedFolderNamePart);

0

上記のように、Path.GetTempPath()はそのための1つの方法です。また、Environment.GetEnvironmentVariable( "TEMP")を呼び出すこともできます。ユーザーがTEMP環境変数を設定している場合は、ます。

アプリケーションでデータを永続化する手段として一時ディレクトリを使用することを計画している場合は、IsolatedStorageを構成/状態/その他のリポジトリとして使用することを検討する必要があります...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.