Tạo một thư mục tạm thời trong Windows?


126

Cách tốt nhất để có được một tên thư mục tạm thời trong Windows là gì? Tôi thấy rằng tôi có thể sử dụng GetTempPathGetTempFileNametạo một tệp tạm thời, nhưng có bất kỳ mkdtempchức năng nào tương đương với chức năng Linux / BSD để tạo một thư mục tạm thời không?


Câu hỏi này có vẻ hơi khó tìm. Cụ thể, nó không hiển thị nếu bạn nhập những thứ như "thư mục temp .net" trong hộp tìm kiếm Stack Overflow. Điều đó có vẻ đáng tiếc, vì các câu trả lời cho đến nay là tất cả các câu trả lời .NET. Bạn có nghĩ rằng bạn có thể thêm thẻ ".net" không? (Và có thể thẻ "thư mục" hoặc "thư mục tạm thời"?) Hoặc có thể thêm từ ".NET" vào tiêu đề? Cũng có thể trong phần thân câu hỏi thay thế nói "tạm thời" với "tạm thời" - vì vậy nếu bạn tìm kiếm dạng ngắn hơn, bạn vẫn sẽ có được kết quả tìm kiếm văn bản tốt. Tôi dường như không có đủ đại diện để tự làm những việc này. Cảm ơn.
Chris

Chà, tôi đang tìm kiếm một câu trả lời không phải là .NET, vì vậy tôi muốn loại bỏ nó ra, nhưng tôi đã thực hiện các chỉnh sửa khác mà bạn đề xuất. Cảm ơn.
Josh Kelley

@Josh Kelley: Tôi chỉ kiểm tra hai lần API Win32 và các tùy chọn duy nhất là theo một cách tiếp cận tương tự là lấy đường dẫn tạm thời, tạo tên tệp ramdom và sau đó tạo thư mục.
Scott Dorman

Câu trả lời:


230

Không, không có tương đương với mkdtemp. Tùy chọn tốt nhất là sử dụng kết hợp GetTempPathGetRandomFileName .

Bạn sẽ cần mã tương tự như thế này:

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

3
Điều này có vẻ hơi nguy hiểm. Cụ thể, có một cơ hội (nhỏ, nhưng khác không, phải không?) Mà Path.Combine (Path.GetTempPath (), Path.GetRandomFileName ()) sẽ trả về tên của một thư mục đã tồn tại. Vì Directory.CreateDirectory (tempDirectory) sẽ không ném ngoại lệ nếu tempDirectory đã tồn tại, trường hợp này sẽ không được ứng dụng của bạn phát hiện. Và sau đó bạn có thể có hai ứng dụng bước vào công việc của nhau. Có sự thay thế nào an toàn hơn trong .NET không?
Chris

22
@Chris: Phương thức GetRandomFileName trả về một chuỗi ngẫu nhiên, mạnh mẽ về mật mã có thể được sử dụng làm tên thư mục hoặc tên tệp. Tôi cho rằng về mặt lý thuyết có thể là đường dẫn kết quả có thể tồn tại, nhưng không có cách nào khác để làm điều này. Bạn có thể kiểm tra xem đường dẫn có tồn tại không và liệu nó có gọi Path.GetRandomFileName () lần nữa không và lặp lại.
Scott Dorman

5
@Chris: Có, nếu bạn lo lắng về bảo mật đến mức đó thì rất có thể một quy trình khác có thể tạo thư mục giữa các lệnh gọi Path.Combine và Directory.CreateDirectory.
Scott Dorman

8
GetRandomFileName tạo ra 11 chữ cái và chữ số ngẫu nhiên có nghĩa là kích thước miền là (26 + 10) ^ 11 = ~ 57 bit. Bạn luôn có thể thực hiện hai cuộc gọi để vuông nó
Marty Neal

27
Ngay sau khi bạn kiểm tra xem thư mục không tồn tại, thần có thể tạm dừng vũ trụ, lẻn vào và tạo cùng một thư mục với tất cả các tệp bạn sắp viết, khiến ứng dụng của bạn bị nổ tung, chỉ làm hỏng ngày của bạn.
Triynko

25

Tôi hack Path.GetTempFileName()để cung cấp cho tôi một filepath hợp lệ, giả ngẫu nhiên trên đĩa, sau đó xóa tệp và tạo một thư mục có cùng đường dẫn tệp.

Điều này tránh sự cần thiết phải kiểm tra xem filepath có sẵn trong một khoảng thời gian hay vòng lặp hay không, theo nhận xét của Chris về câu trả lời của Scott Dorman.

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

  return tempFolder;
}

Nếu bạn thực sự cần một tên ngẫu nhiên được bảo mật bằng mật mã, bạn có thể muốn điều chỉnh câu trả lời của Scott để sử dụng vòng lặp while hoặc do để tiếp tục cố gắng tạo đường dẫn trên đĩa.


7

Tôi thích sử dụng GetTempPath (), một hàm tạo GUID như CoCreateGuid () và CreateDirectory ().

GUID được thiết kế để có xác suất duy nhất cao và cũng rất khó có khả năng ai đó sẽ tự tạo một thư mục có dạng tương tự như GUID (và nếu họ làm thì CreatDirectory () sẽ không thể hiện sự tồn tại của nó.)


5

@Chris. Tôi cũng bị ám ảnh với rủi ro từ xa rằng một thư mục tạm thời có thể đã tồn tại. Các cuộc thảo luận về ngẫu nhiên và mạnh mẽ về mật mã cũng không hoàn toàn thỏa mãn tôi.

Cách tiếp cận của tôi dựa trên thực tế cơ bản là O / S không được phép 2 cuộc gọi để tạo một tệp để cả hai thành công. Một điều đáng ngạc nhiên là các nhà thiết kế .NET đã chọn ẩn chức năng API Win32 cho các thư mục, điều này làm cho việc này dễ dàng hơn nhiều, vì nó trả về lỗi khi bạn cố gắng tạo thư mục lần thứ hai. Đây là những gì tôi sử dụng:

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

Bạn có thể quyết định liệu "chi phí / rủi ro" của mã p / invoke không được quản lý có xứng đáng hay không. Hầu hết sẽ nói rằng nó không phải, nhưng ít nhất bây giờ bạn có một sự lựa chọn.

CreateParentFolder () được để lại như một bài tập cho học sinh. Tôi sử dụng Directory.CreateDirectory (). Hãy cẩn thận nhận cha mẹ của một thư mục, vì nó là null khi ở gốc.


5

Tôi thường sử dụng cái này:

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

Nếu bạn muốn hoàn toàn chắc chắn rằng tên thư mục này sẽ không tồn tại trong đường dẫn tạm thời thì bạn cần kiểm tra xem tên thư mục duy nhất này có tồn tại không và cố gắng tạo tên khác nếu nó thực sự tồn tại.

Nhưng việc thực hiện dựa trên GUID này là đủ. Tôi không có kinh nghiệm với bất kỳ vấn đề trong trường hợp này. Một số ứng dụng MS cũng sử dụng các thư mục tạm thời dựa trên GUID.


1

GetTempPath là cách làm chính xác; Tôi không chắc mối quan tâm của bạn về phương pháp này là gì. Sau đó, bạn có thể sử dụng CreatDirectory để tạo ra nó.


Một vấn đề là GetTempFileName sẽ tạo một tệp không byte. Bạn cần sử dụng GetTempPath, GetRandomFileName và CreateDirectory thay thế.
Scott Dorman

Đó là tốt, có thể, và có thể làm được. Tôi sẽ cung cấp mã nhưng Dorman đã nhận được nó trước tôi và nó hoạt động chính xác.

1

Dưới đây là một cách tiếp cận mạnh mẽ hơn để giải quyết vấn đề va chạm cho các tên thư mục tạm thời. Nó không phải là một cách tiếp cận không thể sai lầm, nhưng nó làm giảm đáng kể khả năng xảy ra va chạm đường dẫn thư mục.

Người ta có khả năng có thể thêm thông tin liên quan đến quá trình hoặc lắp ráp khác vào tên thư mục để làm cho xung đột thậm chí ít xảy ra hơn, mặc dù làm cho thông tin đó hiển thị trên tên thư mục tạm thời có thể không được mong muốn. Người ta cũng có thể trộn thứ tự mà các trường liên quan đến thời gian được kết hợp để làm cho tên thư mục trông ngẫu nhiên hơn. Cá nhân tôi thích để nó theo cách đó đơn giản vì tôi dễ dàng tìm thấy tất cả chúng trong quá trình gỡ lỗi.

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

Như đã đề cập ở trên, Path.GetTempPath () là một cách để làm điều đó. Bạn cũng có thể gọi Môi trường.GetEn Môi trường Biến đổi ("TEMP") trường.GetEn Môi trường Biến đổi nếu người dùng có biến môi trường TEMP được thiết lập.

Nếu bạn đang dự định sử dụng thư mục temp như một phương tiện lưu trữ dữ liệu trong ứng dụng, có lẽ bạn nên xem xét việc sử dụng biệt lập như một kho lưu trữ cho cấu hình / trạng thái / vv ...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.