Skip to main content

C# FileStream

FileStream is a fundamental class in C# that provides a powerful way to read from, write to, and manipulate files. It's an essential component of file handling operations in C# applications, allowing developers to work directly with files at a byte level.

Introduction to FileStream​

The FileStream class in C# represents a stream of bytes that is used specifically for file operations. It's found in the System.IO namespace and serves as a bridge between your application and the file system. Unlike some higher-level file handling classes, FileStream gives you fine-grained control over file operations.

Key characteristics of FileStream include:

  • Byte-level access to file data
  • Support for synchronous and asynchronous operations
  • Ability to specify access modes (read, write, read/write)
  • Control over file sharing permissions
  • Buffer management for improved performance

Basic Syntax and Usage​

To use FileStream in your C# application, you first need to include the System.IO namespace:

csharp
using System;
using System.IO;

Creating a FileStream​

Here's the basic syntax for creating a FileStream object:

csharp
FileStream fileStream = new FileStream(path, mode, access, share);

Parameters explained:

  • path: The file path
  • mode: How to open or create the file (FileMode enum)
  • access: Read, write, or both (FileAccess enum)
  • share: How the file can be shared (FileShare enum)

FileStream Operations​

Writing to a File​

Let's look at a simple example of writing bytes to a file:

csharp
using System;
using System.IO;
using System.Text;

class Program
{
static void Main()
{
string filePath = "example.txt";
string textToWrite = "Hello, FileStream!";
byte[] byteArray = Encoding.UTF8.GetBytes(textToWrite);

// Create a FileStream to write to the file
using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
// Write the bytes to the file
fileStream.Write(byteArray, 0, byteArray.Length);

Console.WriteLine($"Successfully wrote {byteArray.Length} bytes to {filePath}");
} // FileStream is automatically closed here due to using statement
}
}

Output:

Successfully wrote 18 bytes to example.txt

In this example:

  1. We convert a string to a byte array
  2. Create a FileStream with write access
  3. Write the byte array to the file
  4. The using statement ensures the filestream is properly closed when done

Reading from a File​

Now let's read the file we just created:

csharp
using System;
using System.IO;
using System.Text;

class Program
{
static void Main()
{
string filePath = "example.txt";

// Create a FileStream to read from the file
using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
// Create a byte array to store the read data
byte[] byteArray = new byte[fileStream.Length];

// Read the bytes from the file
int bytesRead = fileStream.Read(byteArray, 0, byteArray.Length);

// Convert bytes to string and display
string textRead = Encoding.UTF8.GetString(byteArray);

Console.WriteLine($"Read {bytesRead} bytes from {filePath}");
Console.WriteLine($"Content: {textRead}");
}
}
}

Output:

Read 18 bytes from example.txt
Content: Hello, FileStream!

In this example:

  1. We open the file in read mode
  2. Create a byte array sized to the file length
  3. Read the entire file into the byte array
  4. Convert the bytes back to a string
  5. Display the content

FileStream Properties and Methods​

FileStream provides several useful properties and methods:

Key Properties​

  • Length: Gets the length of the stream in bytes
  • Position: Gets or sets the current position in the stream
  • CanRead: Indicates if the stream supports reading
  • CanWrite: Indicates if the stream supports writing
  • CanSeek: Indicates if the stream supports seeking

Important Methods​

  • Read(): Reads a block of bytes from the stream
  • Write(): Writes a block of bytes to the stream
  • Seek(): Sets the position in the stream
  • Flush(): Clears buffers and writes all data to the file
  • Close(): Closes the stream and releases resources

FileMode, FileAccess, and FileShare Enums​

When creating a FileStream, you need to specify how to open the file, what kind of access you need, and how the file should be shared:

FileMode Options​

csharp
// Common FileMode options
FileMode.Create // Creates a new file (overwrites if exists)
FileMode.CreateNew // Creates a new file (throws exception if exists)
FileMode.Open // Opens an existing file (throws exception if not exists)
FileMode.OpenOrCreate // Opens if exists, creates if not
FileMode.Append // Opens and seeks to the end, or creates
FileMode.Truncate // Opens and clears content (file must exist)

FileAccess Options​

csharp
// FileAccess options
FileAccess.Read // Read access
FileAccess.Write // Write access
FileAccess.ReadWrite // Both read and write access

FileShare Options​

csharp
// Common FileShare options
FileShare.None // No sharing (locks file)
FileShare.Read // Other processes can read
FileShare.Write // Other processes can write
FileShare.ReadWrite // Other processes can read and write

Advanced FileStream Usage​

Working with File Positions​

FileStream allows you to move around within a file using the Seek method and Position property:

csharp
using System;
using System.IO;
using System.Text;

class Program
{
static void Main()
{
string filePath = "positions.txt";

// Create and write to the file
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
byte[] data = Encoding.UTF8.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
fs.Write(data, 0, data.Length);
}

// Read specific portions of the file
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
// Create a small buffer for reading
byte[] buffer = new byte[5];

// Read the first 5 characters
fs.Read(buffer, 0, 5);
Console.WriteLine("First 5 characters: " + Encoding.UTF8.GetString(buffer));

// Move to position 10
fs.Position = 10;

// Read 5 characters starting from position 10
fs.Read(buffer, 0, 5);
Console.WriteLine("5 characters from position 10: " + Encoding.UTF8.GetString(buffer));

// Move 5 positions forward from current position
fs.Seek(5, SeekOrigin.Current);

// Read 5 more characters
fs.Read(buffer, 0, 5);
Console.WriteLine("5 characters after seeking forward 5: " + Encoding.UTF8.GetString(buffer));
}
}
}

Output:

First 5 characters: ABCDE
5 characters from position 10: KLMNO
5 characters after seeking forward 5: UVWXY

Asynchronous File Operations​

For better performance in applications, especially when dealing with large files, you can use the asynchronous methods:

csharp
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;

class Program
{
static async Task Main()
{
string filePath = "async_example.txt";
string textToWrite = "This is an asynchronous file operation example.";
byte[] byteArray = Encoding.UTF8.GetBytes(textToWrite);

// Write asynchronously
using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write,
FileShare.None, bufferSize: 4096, useAsync: true))
{
Console.WriteLine("Writing to file asynchronously...");
await fileStream.WriteAsync(byteArray, 0, byteArray.Length);
Console.WriteLine("Write operation completed.");
}

// Read asynchronously
using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read,
FileShare.Read, bufferSize: 4096, useAsync: true))
{
byte[] buffer = new byte[fileStream.Length];
Console.WriteLine("Reading from file asynchronously...");
await fileStream.ReadAsync(buffer, 0, buffer.Length);
string content = Encoding.UTF8.GetString(buffer);
Console.WriteLine($"Read completed. Content: {content}");
}
}
}

Output:

Writing to file asynchronously...
Write operation completed.
Reading from file asynchronously...
Read completed. Content: This is an asynchronous file operation example.

Buffered File Operations​

You can improve performance by setting an appropriate buffer size:

csharp
// Create a FileStream with a 8KB buffer
using (FileStream fs = new FileStream("large_file.dat", FileMode.Create,
FileAccess.Write, FileShare.None, bufferSize: 8192))
{
// File operations here will use the specified buffer
}

Real-World Application: Simple File Copy​

Let's create a file copying utility that demonstrates FileStream usage in a practical scenario:

csharp
using System;
using System.IO;
using System.Diagnostics;

class Program
{
static void Main()
{
string sourceFile = "source.txt";
string destinationFile = "destination.txt";

// Create a sample source file if it doesn't exist
if (!File.Exists(sourceFile))
{
using (FileStream fs = new FileStream(sourceFile, FileMode.Create, FileAccess.Write))
{
byte[] data = new byte[1024 * 1024]; // 1MB of data
for (int i = 0; i < data.Length; i++)
{
data[i] = (byte)(i % 256);
}
fs.Write(data, 0, data.Length);
}
Console.WriteLine($"Created sample file: {sourceFile} (1MB)");
}

Console.WriteLine($"Copying {sourceFile} to {destinationFile}...");

// Start timing
Stopwatch stopwatch = Stopwatch.StartNew();

// Copy the file using FileStream
using (FileStream sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read))
using (FileStream destinationStream = new FileStream(destinationFile, FileMode.Create, FileAccess.Write))
{
byte[] buffer = new byte[4096]; // 4KB buffer
int bytesRead;
long totalBytes = 0;

// Read and write in chunks until end of file
while ((bytesRead = sourceStream.Read(buffer, 0, buffer.Length)) > 0)
{
destinationStream.Write(buffer, 0, bytesRead);
totalBytes += bytesRead;
}

stopwatch.Stop();

Console.WriteLine($"Copy completed successfully!");
Console.WriteLine($"Copied {totalBytes:N0} bytes in {stopwatch.ElapsedMilliseconds} ms");
Console.WriteLine($"Transfer rate: {(totalBytes / 1024.0 / 1024.0) / (stopwatch.ElapsedMilliseconds / 1000.0):F2} MB/s");
}
}
}

Output (will vary based on system performance):

Created sample file: source.txt (1MB)
Copying source.txt to destination.txt...
Copy completed successfully!
Copied 1,048,576 bytes in 23 ms
Transfer rate: 43.85 MB/s

This example:

  1. Creates a 1MB sample file if it doesn't exist
  2. Opens the source file for reading
  3. Creates the destination file for writing
  4. Uses a 4KB buffer to read and write data in chunks
  5. Measures and reports the performance of the copy operation

Common FileStream Errors and How to Handle Them​

When working with FileStream, you might encounter various exceptions:

csharp
using System;
using System.IO;

class Program
{
static void Main()
{
try
{
// Try to open a non-existent file
using (FileStream fs = new FileStream("non_existent.txt", FileMode.Open))
{
// Code here won't execute if the file doesn't exist
}
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"File not found: {ex.Message}");
}

try
{
// Try to write to a read-only file
string readOnlyFile = "readonly.txt";

// Create and mark as read-only
File.WriteAllText(readOnlyFile, "This is a read-only file");
File.SetAttributes(readOnlyFile, FileAttributes.ReadOnly);

using (FileStream fs = new FileStream(readOnlyFile, FileMode.Open, FileAccess.Write))
{
// Code here won't execute due to access denied
}
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine($"Access denied: {ex.Message}");
}
finally
{
// Clean up
if (File.Exists("readonly.txt"))
{
File.SetAttributes("readonly.txt", FileAttributes.Normal);
File.Delete("readonly.txt");
}
}
}
}

Output:

File not found: Could not find file 'non_existent.txt'.
Access denied: Access to the path 'readonly.txt' is denied.

Best Practices for Using FileStream​

  1. Always use the using statement - This ensures that the FileStream is properly closed and resources are released, even if exceptions occur.

  2. Choose the right buffer size - For large files, a larger buffer (e.g., 8KB or more) can improve performance.

  3. Use asynchronous methods - For UI applications or web servers, use ReadAsync and WriteAsync to prevent blocking.

  4. Be specific with FileMode, FileAccess, and FileShare - Only request the permissions you need.

  5. Handle exceptions properly - File operations are prone to various exceptions (file not found, access denied, etc.).

  6. Consider using higher-level classes when appropriate - For simple text file operations, File, StreamReader, and StreamWriter might be more convenient.

Summary​

FileStream is a powerful class in C# that provides direct access to files at a byte level. It offers fine-grained control over file operations, including positioning, buffering, and sharing. Understanding FileStream is essential for efficient file handling in C# applications, especially when working with binary files or requiring precise control over how files are accessed.

Key points to remember:

  • FileStream works with bytes, not text directly
  • It supports both synchronous and asynchronous operations
  • You can control file access, sharing, and buffering
  • Always use proper exception handling for file operations
  • The using statement ensures resources are properly released

Additional Resources​

Exercises​

  1. Create a program that uses FileStream to append data to an existing file without overwriting its contents.

  2. Build a simple file encryption/decryption tool using FileStream that reads a file byte by byte, performs a simple XOR operation with a key, and writes the result to a new file.

  3. Implement a file comparison tool that uses two FileStream instances to compare files byte by byte and report differences.

  4. Create a program that uses FileStream to read a large file in chunks and calculate its MD5 or SHA-256 hash.

  5. Implement a simple file splitter that breaks a large file into smaller chunks of a specified size using FileStream.



If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)