Updated on
To create a temp file in C#, we call Path.GetTempFileName(). It creates an empty file with a unique name in the temp folder and returns the full path.
When we want to choose the file name ourselves, we get the folder with Path.GetTempPath() and combine it with our name.
Let’s start.
How Do We Get the Temp Folder Path in C#?
Path.GetTempPath() returns the current user’s temp folder as a string. The path always ends with a directory separator, so we can pass it straight to Path.Combine().
Where that folder lives depends on the operating system. On Windows, .NET asks the Win32 API, which takes the first value it finds in the TMP variable, then TEMP, then USERPROFILE, and falls back to the Windows directory. On a typical machine that resolves to C:\Users\<name>\AppData\Local\Temp\.
On Linux and macOS, .NET returns the TMPDIR variable when it is set and /tmp/ when it is not. macOS sets TMPDIR to a private folder for each user at login. On Linux, /tmp/ is usually shared by every user on the machine.
The method only reads the value. It does not check that the folder exists or that our process can write to it, so a broken TMP setting shows up later, when we create the first file.
Newer Windows versions make one exception. A process running as SYSTEM, such as a Windows service under the LocalSystem account, gets C:\Windows\SystemTemp, a folder that ordinary processes cannot open. Code that works in our console app can therefore write somewhere else entirely once it runs as a service.
The main purpose of this location is to store information for a short time. For example, we can use it to store temporary files related to a calculation, a backup copy of a file being edited, etc. As the “temp folder” name suggests, files written here are considered transitory, and thus we should not count on them being available after our application exits. While some operating systems periodically clear the temp folder of older files, it is good practice to remove any temporary files we create to help preserve disk space.
For our examples, we will be using the Windows operating system. By default, on Windows, the temp folder is located at %userprofile%\AppData\Local\Temp, where %userprofile% is the environment variable referencing the currently logged-in user. Because the temp folder location varies between operating systems and even between users, the output from our examples will differ slightly between systems but have no fear, the results are the same!Â
Which Way of Creating a Temp File Should We Pick?
It depends on whether we need to control the file name.
When the name does not matter, Path.GetTempFileName() is the shortest route. It creates an empty file with a unique name and a .tmp extension in the temp folder and returns its full path, so the file already exists when we get it back.
When we want our own name or extension, we join Path.GetTempPath() with that name through Path.Combine(). Uniqueness is then our problem: two runs of the same app will both write to the same text.txt. Path.GetRandomFileName() gives us a random name without creating anything.
When a job produces several files, Directory.CreateTempSubdirectory() creates a uniquely named folder for them, and one recursive delete cleans up everything.
Whichever we pick, nothing deletes the file for us. We delete it ourselves, or we open it with FileOptions.DeleteOnClose and let the stream do it when we dispose of it.
First, let’s begin with a helper method, which we will use to exercise the different temporary file creation techniques. It lives in its own TempFileCreator.cs file:
public static class TempFileCreator
{
public static void CreateTempFile(string filePath)
{
using var sw = new StreamWriter(filePath);
sw.Write("Hello from CodeMaze!");
}
}
Here we define a static TempFileCreator class and add a CreateTempFile() method that writes a simple message to a specified file. We use the StreamWriter class to write the message as it provides a straightforward way to write file content.
Creating a Temp File Using the GetTempPath() Method
Now, let’s create a temporary file using Path.GetTempPath() and our CreateTempFile() method:
var tempFile = Path.Combine(Path.GetTempPath(), "text.txt");
Console.WriteLine($"Creating temp file: '{tempFile}'");
TempFileCreator.CreateTempFile(tempFile);
Path.GetTempPath() returns the path of the system-defined Temp folder. It is one of the helpers on the Path class that work with path strings. Then, using Path.Combine(), we join the temporary path with our filename "text.txt". Finally, we call our CreateTempFile() method to create the file:
Creating temp file: 'C:\Users\CodeMaze\AppData\Local\Temp\text.txt'
Now let’s read the file to verify our operation was successful:
Console.WriteLine($"Reading temp file contents: '{tempFile}'");
var tempFileContent = File.ReadAllText(tempFile);
Console.WriteLine(tempFileContent);
File.Delete(tempFile);
Here we make a call to File.ReadAllText() to read in the file contents. It should also be noted that we are cleaning up after ourselves by calling File.Delete() to remove our temporary file:
Reading temp file contents: 'C:\Users\CodeMaze\AppData\Local\Temp\text.txt' Hello from CodeMaze!
Creating a Temp File Using the GetTempFileName() Method
Now let’s take a look at the GetTempFileName() method. This method creates a uniquely named file in the system-defined Temp folder and returns the full path as a string.
The benefit of this method is its uniqueness guarantee. We can be sure the file did not exist before calling the method, and since it creates the file, we know that it now exists and we have sufficient permissions to access it.
Older answers online warn that this method fails after 65,535 files. On Windows, that limit applied in .NET Framework and in .NET versions before 8. Microsoft Learn says: “Starting in .NET 8, the limitation does not exist on any operating system.” The method can still throw an IOException when it cannot find a free name or create the file, so cleaning up after ourselves still matters.
Let’s see it in action:
tempFile = Path.GetTempFileName();
Console.WriteLine($"Temp file '{tempFile}' exists? {File.Exists(tempFile)}");
TempFileCreator.CreateTempFile(tempFile);
Here we call Path.GetTempFileName() to create a temporary file and immediately call File.Exists() to verify the file’s creation. Following that, we use our CreateTempFile() method to add our “Hello from CodeMaze” message:
Temp file 'C:\Users\CodeMaze\AppData\Local\Temp\tmptlagxa.tmp' exists? True
Now, let’s verify the file contents:
Console.WriteLine($"Reading temp file contents: '{tempFile}'");
tempFileContent = File.ReadAllText(tempFile);
Console.WriteLine(tempFileContent);
File.Delete(tempFile);
Once again we call File.ReadAllText() to read the file contents and call File.Delete() to remove our temporary file:
Hello from CodeMaze!
Creating a Temp File Using the GetFolderPath() Method
Now let’s move to a third option for defining a temporary file path. This time we’ll use the Environment.GetFolderPath() method in combination with a custom file name. This method returns the path to system-defined special folders. For a list of available values, be sure to check the Microsoft documentation for the Environment.SpecialFolder enum:
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
tempFile = Path.Combine(appDataPath, "text.txt");
Console.WriteLine($"Creating temp file: '{tempFile}'");
TempFileCreator.CreateTempFile(tempFile);
First, using Environment.GetFolderPath(), we get the path to the LocalApplicationData folder. Then, using Path.Combine(), we create the full path for our “text.txt” file. Then lastly, we use our CreateTempFile() method to write some content:
Creating temp file: 'C:\Users\CodeMaze\AppData\Local\text.txt'
This folder is not the temp folder, and nothing ever clears it, so here deleting the file is entirely on us. We pick LocalApplicationData over ApplicationData on purpose. On Windows, ApplicationData is the roaming profile folder, which can follow a user from machine to machine on networks with roaming profiles, and scratch files have no business travelling with them. A real application would also add its own subfolder rather than write into the root.
Now, let’s verify it:
Console.WriteLine($"Reading temp file contents: '{tempFile}'");
tempFileContent = File.ReadAllText(tempFile);
Console.WriteLine(tempFileContent);
File.Delete(tempFile);
Once again, we call File.ReadAllText() to read our file’s content and write it to the console. We also call File.Delete() to be sure we are cleaning up after ourselves:
Reading temp file contents: 'C:\Users\CodeMaze\AppData\Local\text.txt' Hello from CodeMaze!
How Do We Create a Temp Directory in C#?
A job that unpacks an archive or renders a batch of images produces several files, and tracking each path by hand gets tedious. Since .NET 7, Directory.CreateTempSubdirectory() creates a uniquely named folder inside the temp folder for us:
var tempDirectory = Directory.CreateTempSubdirectory("codemaze-");
tempFile = Path.Combine(tempDirectory.FullName, "text.txt");
Console.WriteLine($"Creating temp file: '{tempFile}'");
TempFileCreator.CreateTempFile(tempFile);
Console.WriteLine(File.ReadAllText(tempFile));
tempDirectory.Delete(recursive: true);
The optional prefix goes at the start of the folder name, which makes our folders easy to spot. The method returns a DirectoryInfo, so we read FullName to build file paths and call Delete(recursive: true) to remove the folder and everything in it in one go:
Creating temp file: 'C:\Users\CodeMaze\AppData\Local\Temp\codemaze-5jzgylzn.lwu\text.txt' Hello from CodeMaze!
On Linux and macOS there is a second reason to use it. The new folder gets owner-only permissions (700), so other users on the machine cannot read what we put there, even when the parent /tmp/ is shared. Our article on managing directories with Directory and DirectoryInfo covers the rest of the folder API.
How Do We Delete a Temp File Automatically?
We said at the start that we should remove the temporary files we create, and every example so far calls File.Delete() by hand. That works until an exception skips the delete call. FileOptions.DeleteOnClose ties the delete to the stream instead:
tempFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
var options = new FileStreamOptions
{
Mode = FileMode.CreateNew,
Access = FileAccess.ReadWrite,
Options = FileOptions.DeleteOnClose
};
using (var stream = new FileStream(tempFile, options))
{
stream.Write("Hello from CodeMaze!"u8);
Console.WriteLine($"Temp file '{tempFile}' exists? {File.Exists(tempFile)}");
}
Console.WriteLine($"Temp file '{tempFile}' exists? {File.Exists(tempFile)}");
Here, we build a random name with Path.GetRandomFileName() and open the file with FileMode.CreateNew, so the call fails rather than overwrite an existing file. Then we write our message while the stream is open. When the using block ends and the stream is disposed, the file is deleted:
Temp file 'C:\Users\CodeMaze\AppData\Local\Temp\u54hhihl.fbb' exists? True Temp file 'C:\Users\CodeMaze\AppData\Local\Temp\u54hhihl.fbb' exists? False
The deletion is tied to disposal, so the habits from our article on managing IDisposable objects apply here too. The option has limits, though. The file lives only as long as that one stream, so it suits scratch data we write and read back through the same stream, not a file we hand to another process. If the process is killed outright, the result depends on the operating system. Windows deletes the file when its last handle closes, and it closes every handle of a process that dies. On Linux and macOS, .NET deletes the file itself when the stream is disposed, so a killed process leaves it behind in the temp folder.
Conclusion
We’ve seen where the temp folder lives on Windows, Linux and macOS and how Path.GetTempPath() finds it. We created temp files with our own name, with Path.GetTempFileName() and in a special folder, and we grouped several files in a folder from Directory.CreateTempSubdirectory().
None of these methods cleans up for us. A File.Delete() call, a recursive folder delete or FileOptions.DeleteOnClose keeps the temp folder from filling up with files nobody remembers.
Tested with .NET 10 (SDK 10.0.302, runtime 10.0.10) and xUnit 2.9.3.
