สร้างไดเร็กทอรีชั่วคราวใน Windows?


127

วิธีที่ดีที่สุดในการรับชื่อไดเรกทอรีชั่วคราวใน Windows คืออะไร ฉันเห็นว่าฉันสามารถใช้GetTempPathและGetTempFileNameสร้างไฟล์ชั่วคราวได้ แต่มีmkdtempฟังก์ชันใดเทียบเท่ากับฟังก์ชันLinux / BSD สำหรับการสร้างไดเร็กทอรีชั่วคราวหรือไม่


คำถามนี้ดูเหมือนจะหายากเล็กน้อย โดยเฉพาะอย่างยิ่งจะไม่ปรากฏขึ้นหากคุณพิมพ์ "temp directory .net" ในช่องค้นหา Stack Overflow ดูเหมือนจะโชคร้ายเนื่องจากคำตอบนั้นเป็นคำตอบของ. NET ทั้งหมด คุณคิดว่าจะเพิ่มแท็ก ".net" ได้ไหม (และอาจเป็นแท็ก "ไดเรกทอรี" หรือ "ไดเรกทอรีชั่วคราว") หรืออาจเพิ่มคำว่า ".NET" ในชื่อเรื่อง? ในเนื้อหาคำถามอาจจะพูดว่า "temp" กับ "ชั่วคราว" ก็ได้ดังนั้นหากคุณค้นหาแบบฟอร์มที่สั้นกว่านี้คุณจะยังคงได้การค้นหาข้อความที่ตรงกัน ดูเหมือนฉันจะไม่มีตัวแทนเพียงพอที่จะทำสิ่งเหล่านี้ด้วยตัวเอง ขอบคุณ
คริส

ฉันกำลังมองหาคำตอบที่ไม่ใช่ NET ดังนั้นฉันอยากจะทิ้งมันไป แต่ฉันได้ทำการแก้ไขอื่น ๆ ที่คุณแนะนำ ขอบคุณ
Josh Kelley

@ Josh Kelley: ฉันเพิ่งตรวจสอบ Win32 API อีกครั้งและมีตัวเลือกเดียวที่จะทำตามวิธีการที่คล้ายกันในการรับเส้นทางชั่วคราวสร้างชื่อไฟล์ ramdom จากนั้นสร้างไดเร็กทอรี
Scott Dorman

คำตอบ:


230

ไม่มีไม่มีเทียบเท่ากับ mkdtemp ตัวเลือกที่ดีที่สุดคือการใช้GetTempPathและGetRandomFileName ร่วมกัน

คุณจะต้องมีรหัสที่คล้ายกับสิ่งนี้:

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

3
สิ่งนี้ดูเหมือนจะอันตรายเล็กน้อย โดยเฉพาะอย่างยิ่งมีโอกาส (เล็ก แต่ไม่ใช่ศูนย์ใช่ไหม) ที่ Path.Combine (Path.GetTempPath (), Path.GetRandomFileName ()) จะส่งคืนชื่อของไดเร็กทอรีที่มีอยู่แล้ว เนื่องจาก Directory.CreateDirectory (tempDirectory) จะไม่เกิดข้อยกเว้นหากมี tempDirectory อยู่แล้วแอปพลิเคชันของคุณจะไม่ตรวจพบกรณีนี้ จากนั้นคุณอาจมีแอปพลิเคชั่นสองตัวที่ก้าวข้ามการทำงานของกันและกัน มีทางเลือกอื่นที่ปลอดภัยกว่าใน. NET หรือไม่?
คริส

22
@Chris: เมธอด GetRandomFileName จะส่งคืนสตริงสุ่มที่มีการเข้ารหัสที่แข็งแกร่งซึ่งสามารถใช้เป็นชื่อโฟลเดอร์หรือชื่อไฟล์ ฉันคิดว่าเป็นไปได้ในทางทฤษฎีที่เส้นทางผลลัพธ์อาจมีอยู่แล้ว แต่ไม่มีวิธีอื่นในการทำเช่นนี้ คุณสามารถตรวจสอบเพื่อดูว่ามีเส้นทางอยู่หรือไม่และเรียกใช้ Path.GetRandomFileName () อีกครั้งและทำซ้ำ
Scott Dorman

5
@Chris: ใช่ถ้าคุณกังวลเกี่ยวกับความปลอดภัยในระดับนั้นมีโอกาสน้อยมากที่กระบวนการอื่นสามารถสร้างไดเร็กทอรีระหว่างการเรียก Path.Combine และ Directory.CreateDirectory
Scott Dorman

8
GetRandomFileName สร้างตัวอักษรพิมพ์เล็กและตัวเลขแบบสุ่ม 11 ตัวซึ่งหมายความว่าขนาดโดเมนคือ (26 + 10) ^ 11 = ~ 57 บิต คุณสามารถโทรสองครั้งเพื่อยกกำลังสองได้เสมอ
Marty Neal

27
หลังจากที่คุณตรวจสอบว่าไม่มีไดเร็กทอรีพระเจ้าก็สามารถหยุดจักรวาลชั่วคราวแอบเข้ามาและสร้างไดเร็กทอรีเดียวกันกับไฟล์เดียวกันทั้งหมดที่คุณกำลังจะเขียนทำให้แอพของคุณระเบิดและทำลายวันของคุณ
Triynko

26

ฉันแฮ็กPath.GetTempFileName()เพื่อให้ไฟล์พา ธ แบบสุ่มหลอกที่ถูกต้องบนดิสก์จากนั้นลบไฟล์และสร้างไดเร็กทอรีที่มีพา ธ ไฟล์เดียวกัน

วิธีนี้หลีกเลี่ยงความจำเป็นในการตรวจสอบว่า filepath พร้อมใช้งานในชั่วขณะหรือวนซ้ำตามความคิดเห็นของ Chris ในคำตอบของ Scott Dorman

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

  return tempFolder;
}

หากคุณต้องการชื่อสุ่มที่ปลอดภัยด้วยการเข้ารหัสอย่างแท้จริงคุณอาจต้องการปรับคำตอบของ Scott ให้ใช้ while หรือทำวนซ้ำเพื่อพยายามสร้างเส้นทางบนดิสก์ต่อไป


7

ฉันชอบใช้ GetTempPath () ซึ่งเป็นฟังก์ชันการสร้าง GUID เช่น CoCreateGuid () และ CreateDirectory ()

GUID ได้รับการออกแบบมาให้มีความเป็นไปได้สูงที่จะไม่ซ้ำกันและยังเป็นไปไม่ได้อย่างยิ่งที่จะมีคนสร้างไดเร็กทอรีด้วยรูปแบบเดียวกับ GUID (และหากทำเช่นนั้น CreateDirectory () จะไม่สามารถระบุการมีอยู่ได้)


5

@ Chris ฉันเองก็หมกมุ่นอยู่กับความเสี่ยงจากระยะไกลที่อาจมีไดเรกทอรีชั่วคราวอยู่แล้ว การอภิปรายเกี่ยวกับการสุ่มและการเข้ารหัสที่รัดกุมไม่ทำให้ฉันพอใจ

แนวทางของฉันสร้างขึ้นจากข้อเท็จจริงพื้นฐานที่ว่า O / S ต้องไม่อนุญาตให้มีการเรียก 2 ครั้งเพื่อสร้างไฟล์เพื่อให้ทั้งสองสำเร็จ เป็นเรื่องที่น่าแปลกใจเล็กน้อยที่นักออกแบบ. NET เลือกที่จะซ่อนฟังก์ชัน Win32 API สำหรับไดเร็กทอรีซึ่งทำให้ง่ายขึ้นมากเนื่องจากจะส่งคืนข้อผิดพลาดเมื่อคุณพยายามสร้างไดเร็กทอรีเป็นครั้งที่สอง นี่คือสิ่งที่ฉันใช้:

    [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 () ระวังการรับพาเรนต์ของไดเร็กทอรีเนื่องจากเป็นโมฆะเมื่ออยู่ที่รูท


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

หากคุณต้องการแน่ใจอย่างยิ่งว่าชื่อไดเร็กทอรีนี้จะไม่มีอยู่ใน temp path คุณต้องตรวจสอบว่าชื่อไดเร็กทอรีเฉพาะนี้มีอยู่หรือไม่และพยายามสร้างชื่อไดเร็กทอรีอื่นหากมีอยู่จริง

แต่การใช้งานตาม GUID นี้ก็เพียงพอแล้ว ฉันไม่มีประสบการณ์กับปัญหาใด ๆ ในกรณีนี้ แอปพลิเคชัน MS บางตัวใช้ไดเรกทอรีชั่วคราวตาม GUID ด้วย


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

GetTempPathเป็นวิธีที่ถูกต้องในการดำเนินการ ฉันไม่แน่ใจว่าคุณกังวลเกี่ยวกับวิธีนี้อย่างไร จากนั้นคุณสามารถใช้CreateDirectoryเพื่อสร้างได้


ปัญหาหนึ่งคือ GetTempFileName จะสร้างไฟล์ศูนย์ไบต์ คุณต้องใช้ GetTempPath, GetRandomFileName และ CreateDirectory แทน
Scott Dorman

ซึ่งดีเป็นไปได้และทำได้ ฉันจะให้รหัส แต่ดอร์แมนได้รับมันก่อนฉันและมันทำงานได้อย่างถูกต้อง

0

ดังที่ได้กล่าวมาแล้ว Path.GetTempPath () เป็นวิธีหนึ่งที่ทำได้ คุณยังสามารถเรียกEnvironment.GetEnvironmentVariable ("TEMP")หากผู้ใช้มีการตั้งค่าตัวแปรสภาพแวดล้อม TEMP

หากคุณกำลังวางแผนที่จะใช้ไดเร็กทอรี temp เป็นวิธีการคงอยู่ของข้อมูลในแอ็พพลิเคชันคุณอาจต้องดูที่การใช้IsolatedStorageเป็นที่เก็บสำหรับคอนฟิกูเรชัน / state / etc ...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.