creating files, recursively creating directories

C#

C# Problem Overview


I was reading some file IO tutorials for C# and have managed to write out some files, but what if the path I'm given contains directories?

For example, I want to create the file called data/my_file except data folder doesn't exist.

The line,

BinaryWriter outFile = new BinaryWriter(File.OpenWrite(path));

where path is the above string, crashes with the message that part of the path doesn't exist, which means C# isn't creating them as required.

I would like C# to handle all of the messy directory creation and checking for me instead of me having to parse the path and create all of the necessary directories. Is this possible? Otherwise, is there a snippet of code that I can just copy over into my project which will handle anything I might be overlooking (since I don't know much about file management).

C# Solutions


Solution 1 - C#

System.IO.Directory.CreateDirectory() will create all directories and subdirectories in a specified path, should they not already exist.

You can call it, passing the path, to ensure the folder structure is created prior to writing your file.

Solution 2 - C#

here is how I usually do it

Directory.CreateDirectory(Path.GetDirectoryName(filePath));

^ this should take care of ensuring all necessary folders (regardless if some of them already exist) that precedes your file are created. E.g. if you pass it "c:/a/b/c/data/my file.txt", it should ensure "c:/a/b/c/data" path is created.

Solution 3 - C#

While System.IO.Directory.CreateDirectory() will indeed create directories for you recursively, I came across a situation where I had to come up with my own method. Basically, System.IO doesn't support paths over 260 characters, which forced me to use the Delimon.Win32.IO library, which works with long paths, but doesn't create directories recursively.

Here's the code I used for creating directories recursively:

void CreateDirectoryRecursively(string path)
{
    string[] pathParts = path.Split('\\');

    for (int i = 0; i < pathParts.Length; i++)
    {
        if (i > 0)
            pathParts[i] = Path.Combine(pathParts[i - 1], pathParts[i]);

        if (!Directory.Exists(pathParts[i]))
            Directory.CreateDirectory(pathParts[i]);
    }
}

Solution 4 - C#

So, the above didn't work super well for me for basic directory creation. I modified this a bit to handle common cases for drive letters and a path with a file resource on the end.

	public bool CreateDirectoryRecursively(string path)
	{
		try
		{
			string[] pathParts = path.Split('\\');
			for (var i = 0; i < pathParts.Length; i++)
			{
				// Correct part for drive letters
				if (i == 0 && pathParts[i].Contains(":"))
				{
					pathParts[i] = pathParts[i] + "\\";
				} // Do not try to create last part if it has a period (is probably the file name)
				else if (i == pathParts.Length-1 && pathParts[i].Contains("."))
				{
					return true;
				}
				if (i > 0) { 
					pathParts[i] = Path.Combine(pathParts[i - 1], pathParts[i]);
				}
				if (!Directory.Exists(pathParts[i]))
				{
					Directory.CreateDirectory(pathParts[i]);
				}
			}
			return true;
		}
		catch (Exception ex)
		{
			var recipients = _emailErrorDefaultRecipients;
			var subject = "ERROR: Failed To Create Directories in " + this.ToString() + " path: " + path;
			var errorMessage = Error.BuildErrorMessage(ex, subject);
			Email.SendMail(recipients, subject, errorMessage);
			Console.WriteLine(errorMessage);
			return false;

		}

	}

Solution 5 - C#

For .Net 6 with nullable checks:

var dstFilePath = Path.Combine(Path.GetTempPath(), "foo", "bar.txt");
var dstDirPath = Path.GetDirectoryName(dstFilePath) ??
                         throw new InvalidOperationException("Dst dir is invalid");
Directory.CreateDirectory(dstDirPath);

Solution 6 - C#

Previous answers didn't handle Network paths. Attached code which also handles that.

/// <summary>
/// tests (and creates missing) directories in path containing many 
subDirectories which might not exist.
    /// </summary>
    /// <param name="FN"></param>
    public static string VerifyPath(string FN, out bool AllOK)
    {
        AllOK = true;
        var dir = FolderUtils.GetParent(FN);
        if (!Directory.Exists(dir))//todo - move to folderUtils.TestFullDirectory
        {
            const char DIR = '\\';
            //string dirDel = "" + DIR;

            string[] subDirs = FN.Split(DIR);
            string dir2Check = "";
            int startFrom = 1;//skip "c:\"
            FN = CleanPathFromDoubleSlashes(FN);
            if (FN.StartsWith("" + DIR + DIR))//netPath
                startFrom = 3;//FN.IndexOf(DIR, 2);//skip first two slashes..
            for (int i = 0; i < startFrom; i++)
                dir2Check += subDirs[i] + DIR;//fill in begining

            for (int i = startFrom; i < subDirs.Length - 1; i++)//-1 for the file name..
            {
                dir2Check += subDirs[i] + DIR;
                if (!Directory.Exists(dir2Check))
                    try
                    {
                        Directory.CreateDirectory(dir2Check);
                    }
                    catch { AllOK = false; }
            }
        }
        if (File.Exists(FN))
            FN = FolderUtils.getFirstNonExistingPath(FN);
        if (FN.EndsWith("\\") && !Directory.Exists(FN))
            try { Directory.CreateDirectory(FN); }
            catch
            {
                HLogger.HandleMesssage("couldn't create dir:" + FN, TypeOfExceptions.error, PartsOfSW.FileStructure);
                AllOK = false;
            }

        return FN;

    }

And the "CleanDoubleSlashes function":

  public static string CleanPathFromDoubleSlashes(string basePath)
    {
        if (string.IsNullOrEmpty(basePath) || basePath.Length < 2)//don't clean first \\ of LAN address
            return basePath;
        for (int i = basePath.Length - 1; i > 1; i--)
        {
            if ((basePath[i] == '\\' && basePath[i - 1] == '\\') || (basePath[i] == '/' && basePath[i - 1] == '/'))
            {
                basePath = basePath.Remove(i, 1);//Substring(0, i - 2) + basePath.Substring(i, basePath.Length - 1 - i);
            }
        }
        return basePath;
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMxLDevsView Question on Stackoverflow
Solution 1 - C#Eric J.View Answer on Stackoverflow
Solution 2 - C#H7OView Answer on Stackoverflow
Solution 3 - C#Eternal21View Answer on Stackoverflow
Solution 4 - C#williambqView Answer on Stackoverflow
Solution 5 - C#Ssh QuackView Answer on Stackoverflow
Solution 6 - C#ephraimView Answer on Stackoverflow