Managing and handling the filesystem using .NET Core

packtpartner

Packt

Posted on December 27, 2019

Managing and handling the filesystem using .NET Core

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:


 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;

Enter fullscreen mode Exit fullscreen mode

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

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

Enter fullscreen mode Exit fullscreen mode

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:

    
    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:

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

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

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

Enter fullscreen mode Exit fullscreen mode
  • 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:


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

Enter fullscreen mode Exit fullscreen mode
  • 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:

    
    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.

💖 💪 🙅 🚩
packtpartner
Packt

Posted on December 27, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related