What's the best way to get a temp directory name in Windows? I see that I can use GetTempPath
and GetTempFileName
to create a temporary file, but is there any equivalent to the Linux / BSD mkdtemp
function for creating a temporary directory?
No, there is no equivalent to mkdtemp. The best option is to use a combination of GetTempPath and GetRandomFileName.
You would need code similar to this:
public string GetTemporaryDirectory()
{
string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
if(File.Exist(tempDirectory)) {
return GetTemporaryDirectory();
} else {
Directory.CreateDirectory(tempDirectory);
return tempDirectory;
}
}
Path.GetRandomFileName()
. –
Esculent Directory.CreateDirectory
is idempotent so you don't need to test for existence. If for some reason you're giving people access to your filesystem such that your only defense is randomly-named directories, good luck to you... –
Truelove I hack Path.GetTempFileName()
to give me a valid, pseudo-random filepath on disk, then delete the file, and create a directory with the same file path.
This avoids the need for checking if the filepath is available in a while or loop, per Chris' comment on Scott Dorman's answer.
public string GetTemporaryDirectory()
{
string tempFolder = Path.GetTempFileName();
File.Delete(tempFolder);
Directory.CreateDirectory(tempFolder);
return tempFolder;
}
If you truly need a cryptographically secure random name, you may want to adapt Scott's answer to use a while or do loop to keep trying to create a path on disk.
Delete
and the CreateDirectory
? –
Sherd Since .NET 7:
Directory.CreateTempSubdirectory();
C:\Users\USERNAME\AppData\Local\Temp\yafdhmxq.ngy
Using a custom prefix for the folder name
Directory.CreateTempSubdirectory("prefix_"); // prepends "prefix_" to the created dir name
C:\Users\USERNAME\AppData\Local\Temp\prefix_yrlbuecv.2lc
I used some of the answers and implemented GetTmpDirectory
method this way.
public string GetTmpDirectory()
{
string tmpDirectory;
do
{
tmpDirectory = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));
} while (Directory.Exists(tmpDirectory));
Directory.CreateDirectory(tmpDirectory);
return tmpDirectory;
}
|| File.Exists(tempDirectory)
in there and this is perfect. Aside from the unecessary abbreviation from temp to tmp. Do you really need to save that one character? –
Maladjusted I like to use GetTempPath(), a GUID-creation function like CoCreateGuid(), and CreateDirectory().
A GUID is designed to have a high probability of uniqueness, and it's also highly improbable that someone would manually create a directory with the same form as a GUID (and if they do then CreateDirectory() will fail indicating its existence.)
@Chris. I too was obsessed with the remote risk that a temporary directory might already exist. The discussions about random and cryptographically strong don’t completely satisfy me either.
My approach builds on the fundamental fact that the O/S must not allow 2 calls to create a file to both succeed. It is a little surprising that .NET designers chose to hide the Win32 API functionality for directories, which makes this much easier, because it does return an error when you attempt to create a directory for the second time. Here is what I use:
[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;
}
You get to decide whether the "cost/risk" of unmanaged p/invoke code is worth it. Most would say it is not, but at least you now have a choice.
CreateParentFolder() is left as an exercise to the student. I use Directory.CreateDirectory(). Be careful getting the parent of a directory, since it is null when at the root.
I usually use this:
/// <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;
}
If you want to be absolutely sure that this directory name will not exists in temp path then you need to check if this unique directory name exists and try to create other one if it really exists.
But this GUID-based implementation is sufficient. I have no experience with any problem in this case. Some MS applications uses GUID based temp directories too.
Here is a somewhat more brute-force approach to resolving the collision problem for temporary directory names. It is not an infallible approach, but it reduces significantly the chances of a folder path collision.
One could potentially add other process or assembly related information to the directory name to make the collision even less likely, although making such an information visible on the temporary directory name might not be desirable. One could also mix the order with which the time-related fields are combined to make the folder names look more random. I personally prefer to leave it that way simply because it is easier for me to find them all during debugging.
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);
As mentioned above, Path.GetTempPath() is one way to do it. You could also call Environment.GetEnvironmentVariable("TEMP") if the user has a TEMP environment variable set up.
If you are planning on using the temp directory as a means of persisting data in the application, you probably should look at using IsolatedStorage as a repository for configuration/state/etc...
GetTempPath is the correct way of doing it; I'm not sure what your concern about this method is. You can then use CreateDirectory to make it.
© 2022 - 2024 — McMap. All rights reserved.