loading...

Managing and handling the filesystem using .NET Core

packtpartner profile image Packt ・7 min read

Applications will often need to perform input and output of particular code with files and directories in different environments. The System and System.IO namespaces contain classes for this purpose.

This article is an excerpt from the book C# 8.0 and .NET Core 3.0 - Modern Cross-Platform Development - Fourth Edition written by Mark J. Price. Mark follows a step-by-step approach in the book filled with exciting projects and fascinating theory for the readers in this highly acclaimed franchise.

Handling cross-platform environments and filesystems

Let's explore how to handle cross-platform environments like the differences between Windows and Linux or macOS.

  • Create a new console application named WorkingWithFileSystems in a folder named Chapter09.

  • Save the workspace as Chapter09 and add WorkingWithFileSystems to it.

  • Import the System.IO namespace, and statically import the System.Console, System.IO.Directory, System.Environment, and System.IO.Path types, as shown in the following code:

using System.IO; // types for managing the filesystem
using static System.Console;
using static System.IO.Directory;
using static System.IO.Path;
using static System.Environment;

Paths are different for Windows, macOS, and Linux, so we will start by exploring how .NET Core handles this.

  • Create a static OutputFileSystemInfo method, and write statements to do the following:

    • Output the path and directory separation characters
    • Output the path of the current directory
    • Output some special paths for system files, temporary files, and documents

static void OutputFileSystemInfo()
{
WriteLine("{0,-33} {1}", "Path.PathSeparator", PathSeparator);
WriteLine("{0,-33} {1}", "Path.DirectorySeparatorChar",
DirectorySeparatorChar);
WriteLine("{0,-33} {1}", "Directory.GetCurrentDirectory()",
GetCurrentDirectory());
WriteLine("{0,-33} {1}", "Environment.CurrentDirectory",
CurrentDirectory);
WriteLine("{0,-33} {1}", "Environment.SystemDirectory",
SystemDirectory);
WriteLine("{0,-33} {1}", "Path.GetTempPath()", GetTempPath());
WriteLine("GetFolderPath(SpecialFolder");
WriteLine("{0,-33} {1}", " .System)",
GetFolderPath(SpecialFolder.System));
WriteLine("{0,-33} {1}", " .ApplicationData)",
GetFolderPath(SpecialFolder.ApplicationData));
WriteLine("{0,-33} {1}", " .MyDocuments)",
GetFolderPath(SpecialFolder.MyDocuments));
WriteLine("{0,-33} {1}", " .Personal)",
GetFolderPath(SpecialFolder.Personal));
}

The Environment type has many other useful members, including the GetEnvironmentVariables method and the OSVersion and ProcessorCount properties.

  • In the Main method, call OutputFileSystemInfo, as shown in the following code:

static void Main(string[] args)
{
OutputFileSystemInfo();
}

  • Run the console application and view the result, as shown in the following screenshot when run on Windows:

Alt Text

Windows uses a backslash for the directory separator character. macOS and Linux use a forward slash for the directory separator character.

Managing drives

To manage drives, use DriveInfo, which has a static method that returns information about all the drives connected to your computer. Each drive has a drive type.

  • Create a WorkWithDrives method, and write statements to get all the drives and output their name, type, size, available free space, and format, but only if the drive is ready, as shown in the following code:

static void WorkWithDrives()
{
WriteLine("{0,-30} | {1,-10} | {2,-7} | {3,18} | {4,18}",
"NAME", "TYPE", "FORMAT", "SIZE (BYTES)", "FREE SPACE");
foreach (DriveInfo drive in DriveInfo.GetDrives())
{
if (drive.IsReady)
{
WriteLine(
"{0,-30} | {1,-10} | {2,-7} | {3,18:N0} | {4,18:N0}",
drive.Name, drive.DriveType, drive.DriveFormat,
drive.TotalSize, drive.AvailableFreeSpace);
}
else
{
WriteLine("{0,-30} | {1,-10}", drive.Name, drive.DriveType);
}
}
}

  • In Main, comment out the previous method call, and add a call to WorkWithDrives, as shown in the following code:

static void Main(string[] args)
{
// OutputFileSystemInfo();
WorkWithDrives();
}

  • Run the console application and view the result, as shown in the following screenshot:

Alt Text

Managing directories

To manage directories, use the Directory, Path, and Environment static classes.

These types include many properties and methods for working with the filesystem, as shown in the following diagram:

Alt Text

When constructing custom paths, you must be careful to write your code so that it makes no assumptions about the platform, for example, what to use for the directory separator character.

  • Create a WorkWithDirectories method, and write statements to do the following:

    • Define a custom path under the user's home directory by creating an array of strings for the directory names, and then properly combining them with the Path type's static Combine method.
    • Check for the existence of the custom directory path using the static Exists method of the Directory class.
    • Create, and then delete the directory, including files and subdirectories within it, using the static CreateDirectory and Delete methods of the Directory class.

    static void WorkWithDirectories()
    {
    // define a directory path for a new folder
    // starting in the user's folder
    var newFolder = Combine(
    GetFolderPath(SpecialFolder.Personal),
    "Code", "Chapter09", "NewFolder");
    WriteLine($"Working with: {newFolder}");
    // check if it exists
    WriteLine($"Does it exist? {Exists(newFolder)}");
    // create directory
    WriteLine("Creating it...");
    CreateDirectory(newFolder);
    WriteLine($"Does it exist? {Exists(newFolder)}");
    Write("Confirm the directory exists, and then press ENTER: ");
    ReadLine();
    // delete directory
    WriteLine("Deleting it...");
    Delete(newFolder, recursive: true);
    WriteLine($"Does it exist? {Exists(newFolder)}");
    }

  • In the Main method, comment out the previous method call, and add a call to WorkWithDirectories, as shown in the following code:

static void Main(string[] args)
{
// OutputFileSystemInfo();
// WorkWithDrives();
WorkWithDirectories();
}

  • Run the console application and view the result, and use your favorite file management tool to confirm that the directory has been created before pressing Enter to delete it, as shown in the following output: Working with: /Users/markjprice/Code/Chapter09/NewFolder

Does it exist? False
Creating it...
Does it exist? True
Confirm the directory exists, and then press ENTER:
Deleting it...
Does it exist? False

Managing files

When working with files, you could statically import the File type, just as we did for the Directory type, but, for the next example, we will not, because it has some of the same methods as the Directory type and they would conflict. The File type has a short enough name not to matter in this case.

  • Create a WorkWithFiles method, and write statements to do the following:

    • Check for the existence of a file.
    • Create a text file.
    • Write a line of text to the file.
    • Close the file to release system resources and file locks (this would normally be done inside a try-finally statement block to ensure that the file is closed even if an exception occurs when writing to it).
    • Copy the file to a backup.
    • Delete the original file.
    • Read the backup file's contents and then close it.

    static void WorkWithFiles()
    {
    // define a directory path to output files
    // starting in the user's folder
    var dir = Combine(
    GetFolderPath(SpecialFolder.Personal),
    "Code", "Chapter09", "OutputFiles");
    CreateDirectory(dir);
    // define file paths
    string textFile = Combine(dir, "Dummy.txt");
    string backupFile = Combine(dir, "Dummy.bak");
    WriteLine($"Working with: {textFile}");
    // check if a file exists
    WriteLine($"Does it exist? {File.Exists(textFile)}");
    // create a new text file and write a line to it
    StreamWriter textWriter = File.CreateText(textFile);
    textWriter.WriteLine("Hello, C#!");
    textWriter.Close(); // close file and release resources
    WriteLine($"Does it exist? {File.Exists(textFile)}");
    // copy the file, and overwrite if it already exists
    File.Copy(sourceFileName: textFile,
    destFileName: backupFile, overwrite: true);
    WriteLine(
    $"Does {backupFile} exist? {File.Exists(backupFile)}");
    Write("Confirm the files exist, and then press ENTER: ");
    ReadLine();
    // delete file
    File.Delete(textFile);
    WriteLine($"Does it exist? {File.Exists(textFile)}");
    // read from the text file backup
    WriteLine($"Reading contents of {backupFile}:");
    StreamReader textReader = File.OpenText(backupFile);
    WriteLine(textReader.ReadToEnd());
    textReader.Close();
    }

  • In Main, comment out the previous method call, and add a call to WorkWithFiles.

  • Run the application and view the result, as shown in the following output:

    Working with: /Users/markjprice/Code/Chapter09/OutputFiles/Dummy.txt
    Does it exist? False
    Does it exist? True
    Does /Users/markjprice/Code/Chapter09/OutputFiles/Dummy.bak exist? True
    Confirm the files exist, and then press ENTER:
    Does it exist? False
    Reading contents of /Users/markjprice/Code/Chapter09/OutputFiles/Dummy.bak:
    Hello, C#!

Managing paths

Sometimes, you need to work with parts of a path, for example, you might want to extract just the folder name, the file name, or the extension. Sometimes, you need to generate temporary folders and file names. You can do this with static methods of the Path class.

  • Add the following statements to the end of the WorkWithFiles method:

// Managing paths
WriteLine($"Folder Name: {GetDirectoryName(textFile)}");
WriteLine($"File Name: {GetFileName(textFile)}");
WriteLine("File Name without Extension: {0}",
GetFileNameWithoutExtension(textFile));
WriteLine($"File Extension: {GetExtension(textFile)}");
WriteLine($"Random File Name: {GetRandomFileName()}");
WriteLine($"Temporary File Name: {GetTempFileName()}");

  • Run the application and view the result, as shown in the following output: Folder Name: /Users/markjprice/Code/Chapter09/OutputFiles File Name: Dummy.txt File Name without Extension: Dummy File Extension: .txt Random File Name: u45w1zki.co3 Temporary File Name: /var/folders/tz/xx0y_wld5sx0nv0fjtq4tnpc0000gn/T/tmpyqrepP.tmp

GetTempFileName creates a zero-byte file and returns its name, ready for you to use. GetRandomFileName just returns a filename; it doesn't create the file.

To summarize, we explored how to manage and handle filesystem using .NET Core. If you want to learn the fundamentals, how to build practical applications, and the latest features of C# 8.0 and .NET Core 3.0, check out our latest book C# 8.0 and .NET Core 3.0 - Modern Cross-Platform Development - Fourth Edition written by Mark J. Price.

About the author

Mark J. Price is a Microsoft Specialist: Programming in C# and Architecting Microsoft Azure Solutions, with more than 20 years of educational and programming experience. Since 1993, Mark has passed more than 80 Microsoft programming exams and specializes in preparing others to pass them too. His students range from professionals with decades of experience to 16-year old apprentices with none. He successfully guides all of them by combining educational skills with real-world experience in consulting and developing systems for enterprises worldwide.

Posted on Dec 27 '19 by:

packtpartner profile

Packt

@packtpartner

Founded in 2004 in Birmingham, U.K. Packt's mission is to help the world put software to work in new ways, through the delivery of effective learning and information services to IT professionals.

Discussion

markdown guide