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:
using System;
using System.IO;
Creating a FileStream
Here's the basic syntax for creating a FileStream object:
FileStream fileStream = new FileStream(path, mode, access, share);
Parameters explained:
path
: The file pathmode
: 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:
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:
- We convert a string to a byte array
- Create a FileStream with write access
- Write the byte array to the file
- The
using
statement ensures the filestream is properly closed when done
Reading from a File
Now let's read the file we just created:
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:
- We open the file in read mode
- Create a byte array sized to the file length
- Read the entire file into the byte array
- Convert the bytes back to a string
- Display the content
FileStream Properties and Methods
FileStream provides several useful properties and methods:
Key Properties
Length
: Gets the length of the stream in bytesPosition
: Gets or sets the current position in the streamCanRead
: Indicates if the stream supports readingCanWrite
: Indicates if the stream supports writingCanSeek
: Indicates if the stream supports seeking
Important Methods
Read()
: Reads a block of bytes from the streamWrite()
: Writes a block of bytes to the streamSeek()
: Sets the position in the streamFlush()
: Clears buffers and writes all data to the fileClose()
: 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
// 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
// FileAccess options
FileAccess.Read // Read access
FileAccess.Write // Write access
FileAccess.ReadWrite // Both read and write access
FileShare Options
// 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:
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:
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:
// 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:
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:
- Creates a 1MB sample file if it doesn't exist
- Opens the source file for reading
- Creates the destination file for writing
- Uses a 4KB buffer to read and write data in chunks
- 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:
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
-
Always use the
using
statement - This ensures that the FileStream is properly closed and resources are released, even if exceptions occur. -
Choose the right buffer size - For large files, a larger buffer (e.g., 8KB or more) can improve performance.
-
Use asynchronous methods - For UI applications or web servers, use
ReadAsync
andWriteAsync
to prevent blocking. -
Be specific with FileMode, FileAccess, and FileShare - Only request the permissions you need.
-
Handle exceptions properly - File operations are prone to various exceptions (file not found, access denied, etc.).
-
Consider using higher-level classes when appropriate - For simple text file operations,
File
,StreamReader
, andStreamWriter
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
-
Create a program that uses FileStream to append data to an existing file without overwriting its contents.
-
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.
-
Implement a file comparison tool that uses two FileStream instances to compare files byte by byte and report differences.
-
Create a program that uses FileStream to read a large file in chunks and calculate its MD5 or SHA-256 hash.
-
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 [email protected]. I’d greatly appreciate your feedback! :)