Zircon.IO
2.0.0
dotnet add package Zircon.IO --version 2.0.0
NuGet\Install-Package Zircon.IO -Version 2.0.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Zircon.IO" Version="2.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Zircon.IO" Version="2.0.0" />
<PackageReference Include="Zircon.IO" />
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Zircon.IO --version 2.0.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: Zircon.IO, 2.0.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Zircon.IO@2.0.0
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Zircon.IO&version=2.0.0
#tool nuget:?package=Zircon.IO&version=2.0.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
Zircon.IO
I/O extension methods for streams providing convenient async utilities for reading and manipulating stream data efficiently and safely.
Features
- Async Stream Operations: Asynchronous methods for better performance and scalability
- Memory Efficient: Uses MemoryStream for efficient byte array creation
- Simple API: Clean, intuitive extension methods
- Exception Safe: Proper resource disposal and exception handling
- Universal Compatibility: Works with any Stream implementation
Installation
dotnet add package Zircon.IO
Usage
Basic Stream Reading
using System.IO;
using Zircon.IO;
// Reading from a file stream
using var fileStream = File.OpenRead("document.pdf");
byte[] fileBytes = await fileStream.ReadAllBytesAsync();
// Reading from HTTP response
using var httpClient = new HttpClient();
using var response = await httpClient.GetAsync("https://example.com/api/data");
using var stream = await response.Content.ReadAsStreamAsync();
byte[] responseBytes = await stream.ReadAllBytesAsync();
Web API File Upload Processing
[ApiController]
[Route("api/[controller]")]
public class FileUploadController : ControllerBase
{
[HttpPost("upload")]
public async Task<IActionResult> UploadFile(IFormFile file)
{
if (file == null || file.Length == 0)
return BadRequest("No file provided");
using var stream = file.OpenReadStream();
byte[] fileBytes = await stream.ReadAllBytesAsync();
// Process the file bytes
var result = await _fileProcessingService.ProcessFileAsync(fileBytes, file.FileName);
return Ok(new { FileSize = fileBytes.Length, ProcessedAt = DateTime.UtcNow });
}
}
Database BLOB Operations
public class DocumentService
{
private readonly IDbConnection _connection;
public async Task<byte[]> GetDocumentContentAsync(int documentId)
{
const string sql = "SELECT Content FROM Documents WHERE Id = @Id";
using var command = _connection.CreateCommand();
command.CommandText = sql;
command.Parameters.Add(new SqlParameter("@Id", documentId));
using var reader = await command.ExecuteReaderAsync();
if (await reader.ReadAsync())
{
using var stream = reader.GetStream("Content");
return await stream.ReadAllBytesAsync();
}
throw new DocumentNotFoundException($"Document {documentId} not found");
}
public async Task SaveDocumentAsync(int documentId, Stream contentStream)
{
byte[] content = await contentStream.ReadAllBytesAsync();
const string sql = "UPDATE Documents SET Content = @Content WHERE Id = @Id";
using var command = _connection.CreateCommand();
command.CommandText = sql;
command.Parameters.Add(new SqlParameter("@Id", documentId));
command.Parameters.Add(new SqlParameter("@Content", content));
await command.ExecuteNonQueryAsync();
}
}
Stream Processing Pipeline
public class ImageProcessingService
{
public async Task<byte[]> ProcessImageAsync(Stream inputStream)
{
// Read the input image
byte[] originalBytes = await inputStream.ReadAllBytesAsync();
// Process with image library
using var image = Image.Load(originalBytes);
image.Mutate(x => x.Resize(800, 600));
// Convert back to byte array
using var outputStream = new MemoryStream();
await image.SaveAsJpegAsync(outputStream);
outputStream.Position = 0;
return await outputStream.ReadAllBytesAsync();
}
public async Task<ProcessingResult> ProcessMultipleImagesAsync(IEnumerable<Stream> imageStreams)
{
var tasks = imageStreams.Select(async stream =>
{
var bytes = await stream.ReadAllBytesAsync();
return new ProcessedImage
{
OriginalSize = bytes.Length,
ProcessedData = await ProcessImageBytesAsync(bytes)
};
});
var results = await Task.WhenAll(tasks);
return new ProcessingResult { Images = results };
}
}
Caching and Storage
public class CacheService
{
private readonly IMemoryCache _cache;
public async Task<byte[]> GetOrCreateAsync(string key, Func<Task<Stream>> factory)
{
if (_cache.TryGetValue(key, out byte[] cached))
{
return cached;
}
using var stream = await factory();
var bytes = await stream.ReadAllBytesAsync();
_cache.Set(key, bytes, TimeSpan.FromMinutes(30));
return bytes;
}
}
// Usage
var pdfBytes = await _cacheService.GetOrCreateAsync("report-2023", async () =>
{
return await _reportService.GenerateReportStreamAsync(2023);
});
Network Stream Processing
public class NetworkFileService
{
private readonly HttpClient _httpClient;
public async Task<FileDownloadResult> DownloadFileAsync(string url)
{
using var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
using var stream = await response.Content.ReadAsStreamAsync();
var content = await stream.ReadAllBytesAsync();
return new FileDownloadResult
{
Content = content,
ContentType = response.Content.Headers.ContentType?.MediaType,
FileName = ExtractFileNameFromUrl(url),
Size = content.Length
};
}
public async Task<bool> UploadFileAsync(string url, Stream fileStream)
{
var fileBytes = await fileStream.ReadAllBytesAsync();
using var content = new ByteArrayContent(fileBytes);
using var response = await _httpClient.PostAsync(url, content);
return response.IsSuccessStatusCode;
}
}
Extension Methods
ReadAllBytesAsync
public static async Task<byte[]> ReadAllBytesAsync(this Stream stream)
Description: Asynchronously reads all bytes from the current stream and returns them as a byte array.
Parameters:
stream: The stream to read from
Returns:
Task<byte[]>: A task representing the asynchronous operation, containing all bytes from the stream
Key Features:
- Memory Efficient: Uses MemoryStream internally for optimal memory usage
- Async/Await Compatible: Fully asynchronous operation
- Resource Safe: Properly disposes of internal resources
- Position Independent: Works regardless of current stream position
Performance Characteristics
- Memory Usage: Creates a single MemoryStream internally, minimizing memory allocations
- Async Operation: Non-blocking I/O operations for better scalability
- Buffer Management: Leverages Stream.CopyToAsync for optimal buffering
- GC Pressure: Minimal garbage collection pressure due to efficient implementation
Comparison with Alternatives
| Method | Memory Efficiency | Async Support | Code Simplicity |
|---|---|---|---|
stream.ReadAllBytesAsync() |
High | Yes | Excellent |
| Manual byte[] + Read loop | Medium | Can be added | Poor |
| ReadToEnd + Encoding | Low | Yes | Good |
| Multiple Read calls | Low | Can be added | Poor |
Error Handling
public async Task<byte[]> SafeReadAllBytesAsync(Stream stream)
{
try
{
return await stream.ReadAllBytesAsync();
}
catch (IOException ioEx)
{
_logger.LogError(ioEx, "I/O error while reading stream");
throw new StreamProcessingException("Failed to read stream due to I/O error", ioEx);
}
catch (ObjectDisposedException)
{
_logger.LogWarning("Attempted to read from disposed stream");
throw new InvalidOperationException("Cannot read from disposed stream");
}
catch (NotSupportedException)
{
_logger.LogWarning("Stream does not support reading");
throw new InvalidOperationException("Stream does not support reading");
}
}
Integration with Dependency Injection
public interface IStreamProcessor
{
Task<byte[]> ProcessStreamAsync(Stream input);
}
public class StreamProcessor : IStreamProcessor
{
private readonly ILogger<StreamProcessor> _logger;
public StreamProcessor(ILogger<StreamProcessor> logger)
{
_logger = logger;
}
public async Task<byte[]> ProcessStreamAsync(Stream input)
{
_logger.LogInformation("Processing stream of length: {Length}", input.Length);
var bytes = await input.ReadAllBytesAsync();
_logger.LogInformation("Successfully processed {ByteCount} bytes", bytes.Length);
return bytes;
}
}
// Registration
services.AddScoped<IStreamProcessor, StreamProcessor>();
Best Practices
- Resource Management: Always use
usingstatements with streams - Exception Handling: Wrap in try-catch for production scenarios
- Memory Considerations: Be mindful of large files and available memory
- Stream Position: The method reads from current position to end
- Async Patterns: Use ConfigureAwait(false) in library code if needed
Advanced Scenarios
Streaming Large Files
public async Task<ProcessingResult> ProcessLargeFileAsync(Stream largeFileStream, int maxSize = 100_000_000)
{
if (largeFileStream.Length > maxSize)
{
throw new FileTooLargeException($"File size {largeFileStream.Length} exceeds maximum {maxSize}");
}
var bytes = await largeFileStream.ReadAllBytesAsync();
return await ProcessBytesInChunks(bytes);
}
Combining with Other Extensions
public static async Task<string> ReadAllTextAsync(this Stream stream, Encoding encoding = null)
{
var bytes = await stream.ReadAllBytesAsync();
return (encoding ?? Encoding.UTF8).GetString(bytes);
}
public static async Task<T> ReadJsonAsync<T>(this Stream stream)
{
var bytes = await stream.ReadAllBytesAsync();
return JsonSerializer.Deserialize<T>(bytes);
}
Future Extensions
This package provides a foundation for additional I/O utilities. Future versions may include:
- Stream writing utilities
- Compression/decompression extensions
- Encryption/decryption helpers
- Progress reporting for large operations
- Chunked reading operations
License
This project is licensed under the MIT License - see the LICENSE file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.