FastDFS.Client
1.1.1
dotnet add package FastDFS.Client --version 1.1.1
NuGet\Install-Package FastDFS.Client -Version 1.1.1
<PackageReference Include="FastDFS.Client" Version="1.1.1" />
<PackageVersion Include="FastDFS.Client" Version="1.1.1" />
<PackageReference Include="FastDFS.Client" />
paket add FastDFS.Client --version 1.1.1
#r "nuget: FastDFS.Client, 1.1.1"
#:package FastDFS.Client@1.1.1
#addin nuget:?package=FastDFS.Client&version=1.1.1
#tool nuget:?package=FastDFS.Client&version=1.1.1
FastDFS.Client
A modern, high-performance FastDFS .NET client library with built-in connection pooling and multi-cluster support.
Features
- ✅ High Performance: Uses modern Socket API instead of TcpClient for better throughput and lower latency
- ✅ Target Framework: netstandard2.0 (compatible with .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5+)
- ✅ Connection Pooling: Automatic connection management with configurable pool sizes
- ✅ Shared Connection Pool: Clients with identical configurations share the same connection pool, eliminating redundant TCP connections
- ✅ Multi-Cluster Support: Manage multiple FastDFS clusters with named clients
- ✅ Dynamic Client Registration: Register and remove clients at runtime (e.g., from a database)
- ✅ Full Async/Await: All operations are fully asynchronous
- ✅ Dependency Injection: First-class DI support with IOptions pattern
- ✅ Non-DI Support: Factory pattern for non-DI scenarios
- ✅ Logging Integration: Built-in logging with
Microsoft.Extensions.Logging - ✅ Automatic Failover: Tracker server failover support
- ✅ Low-Level Access: Direct access to
ITrackerClientandIStorageClientfor advanced use cases - ✅ Zero Dependencies: Core library has no external dependencies
- ✅ Comprehensive Tests: 86+ unit tests with 100% pass rate
Architecture
The SDK is structured in three clear layers:
IFastDFSClient (Coordination Layer)
├── ITrackerClient — Queries tracker servers to locate storage nodes
└── IStorageClient — Executes protocol commands against storage servers
and manages per-node connection pools
IFastDFSClient— Main API. Coordinates tracker queries and storage operations. This is what you inject and use day-to-day.ITrackerClient— Communicates with tracker servers to resolve storage node addresses. Handles automatic failover across multiple trackers.IStorageClient— Executes FastDFS binary protocol commands against a specific storage server. Manages a dynamic connection pool per storage node.
Quick Start
Installation
dotnet add package FastDFS.Client
dotnet add package FastDFS.Client.DependencyInjection # for DI support
Basic Usage (Dependency Injection)
Single Cluster:
// Program.cs
services.AddFastDFS(options =>
{
options.TrackerServers = new[] { "192.168.1.100:22122" };
options.ConnectionPool = new ConnectionPoolConfiguration
{
MaxConnectionPerServer = 50,
MinConnectionPerServer = 5
};
});
// Usage in your service
public class FileService
{
private readonly IFastDFSClient _client;
public FileService(IFastDFSClient client)
{
_client = client;
}
public async Task<string> UploadFile(byte[] content, string extension)
{
// Upload returns file ID like: group1/M00/00/00/wKgBaGVlYWRlYS5qcGc
return await _client.UploadAsync(null, content, extension);
}
}
Multiple Clusters:
// Register multiple clusters
services.AddFastDFS("default", options =>
{
options.TrackerServers = new[] { "192.168.1.100:22122" };
});
services.AddFastDFS("backup", options =>
{
options.TrackerServers = new[] { "192.168.2.100:22122" };
});
// Usage
public class FileService
{
private readonly IFastDFSClient _defaultClient;
private readonly IFastDFSClient _backupClient;
public FileService(IFastDFSClientFactory factory)
{
_defaultClient = factory.GetClient("default");
_backupClient = factory.GetClient("backup");
}
}
Factory API:
// Get default client
var defaultClient = factory.GetClient();
// Get named client
var backupClient = factory.GetClient("backup");
// Check if a client exists
if (factory.HasClient("cdn"))
{
var cdnClient = factory.GetClient("cdn");
}
// Get all client names
var clientNames = factory.GetClientNames();
// Register client dynamically at runtime
factory.RegisterClient("new-cluster", new FastDFSConfiguration
{
TrackerServers = new[] { "192.168.3.100:22122" }
});
// Remove a client
factory.RemoveClient("backup");
// Register without any pre-configured clients (pure dynamic registration)
// services.AddFastDFS(); — then register all clients at runtime via RegisterClient
Configuration (appsettings.json)
{
"FastDFS": {
"Clusters": {
"default": {
"TrackerServers": ["192.168.1.100:22122", "192.168.1.101:22122"],
"ConnectionPool": {
"MaxConnectionPerServer": 50,
"MinConnectionPerServer": 5,
"ConnectionIdleTimeout": 300,
"ConnectionLifetime": 3600
},
"NetworkTimeout": 30,
"Charset": "UTF-8"
},
"backup": {
"TrackerServers": ["192.168.2.100:22122"],
"ConnectionPool": {
"MaxConnectionPerServer": 30,
"MinConnectionPerServer": 3
}
}
}
}
}
services.AddFastDFS(configuration.GetSection("FastDFS"));
Non-DI Usage
IFastDFSClient implements IDisposable. Always dispose the client (or use using) to release TCP connections cleanly.
Single client:
var options = new FastDFSConfiguration
{
TrackerServers = new[] { "192.168.1.100:22122" }
};
using var client = FastDFSClientBuilder.CreateClient(options);
var fileId = await client.UploadAsync(null, fileBytes, "jpg");
var content = await client.DownloadAsync(fileId);
await client.DeleteAsync(fileId);
Multiple Clusters (Non-DI):
Use FastDFSClientManager for multi-cluster scenarios — it owns and disposes all clients.
using var manager = new FastDFSClientManager();
manager.AddClient("default", new FastDFSConfiguration
{
TrackerServers = new[] { "192.168.1.100:22122" }
});
manager.AddClient("backup", new FastDFSConfiguration
{
TrackerServers = new[] { "192.168.2.100:22122" }
});
var defaultClient = manager.GetClient("default");
var backupClient = manager.GetClient("backup");
// Register client dynamically
manager.RegisterClient("new-cluster", new FastDFSConfiguration
{
TrackerServers = new[] { "192.168.3.100:22122" }
});
// Remove client
manager.RemoveClient("backup");
// Dispose manager → disposes all managed clients and TCP connections
API Overview
Upload Operations
// Upload from byte array (auto-select group)
string fileId = await client.UploadAsync(null, bytes, "jpg");
// Returns: "group1/M00/00/00/wKgBaGVlYWRlYS5qcGc"
// Upload to specific group
string fileId = await client.UploadAsync("group1", bytes, "jpg");
// Upload from stream
string fileId = await client.UploadAsync(null, stream, "pdf");
// Upload from file path
string fileId = await client.UploadFileAsync(null, "/path/to/file.png");
// Upload appender file (supports append later)
string fileId = await client.UploadAppenderFileAsync(null, bytes, "log");
// Append to appender file
await client.AppendFileAsync(fileId, newBytes);
Download Operations
// Download to byte array
byte[] content = await client.DownloadAsync(fileId);
// Download to stream
await client.DownloadAsync(fileId, outputStream);
// Download to file
await client.DownloadFileAsync(fileId, "/save/path.jpg");
// Partial download (offset and length)
byte[] partial = await client.DownloadAsync(fileId, offset: 1024, length: 2048);
File Management
// Query file info
FastDFSFileInfo info = await client.QueryFileInfoAsync(fileId);
Console.WriteLine($"Size: {info.FileSize}, CRC32: {info.Crc32}");
// Check if file exists
bool exists = await client.FileExistsAsync(fileId);
// Delete file
await client.DeleteAsync(fileId);
// Set metadata
var metadata = new FastDFSMetadata
{
{ "author", "John" },
{ "created", "2024-01-01" },
{ "width", "1920" },
{ "height", "1080" }
};
await client.SetMetadataAsync(fileId, metadata, MetadataFlag.Overwrite);
// Get metadata
FastDFSMetadata metadata = await client.GetMetadataAsync(fileId);
string author = metadata["author"];
Advanced Usage
Low-Level Tracker and Storage Access
IFastDFSClient exposes TrackerClient and StorageClient for scenarios that require direct control over the underlying protocol.
// Query the tracker directly — get storage server info for upload
StorageServerInfo server = await client.TrackerClient.QueryStorageForUploadAsync("group1");
// Execute storage operation directly against the resolved server
string fileId = await client.StorageClient.UploadAsync(server, bytes, "jpg");
// Query all available storage servers for a file (for custom selection logic)
var servers = await client.TrackerClient.QueryAllStoragesForDownloadAsync("group1", fileName);
// Execute a download against a specific server
byte[] content = await client.StorageClient.DownloadAsync(server, "group1", fileName, offset: 0, length: 0);
This is useful for:
- Custom storage server selection logic
- Batch operations targeting a single storage node
- Implementing custom retry or circuit-breaker policies at the protocol level
Connection Pool Configuration
services.AddFastDFS(options =>
{
options.TrackerServers = new[] { "192.168.1.100:22122", "192.168.1.101:22122" };
options.ConnectionPool = new ConnectionPoolConfiguration
{
MaxConnectionPerServer = 50, // Maximum connections per server
MinConnectionPerServer = 5, // Minimum connections (pre-warmed)
ConnectionIdleTimeout = 300, // Idle timeout in seconds
ConnectionLifetime = 3600, // Max lifetime in seconds
ConnectionTimeout = 30000, // Connection timeout in ms
SendTimeout = 30000, // Send timeout in ms
ReceiveTimeout = 30000 // Receive timeout in ms
};
options.Charset = "UTF-8";
options.NetworkTimeout = 30;
});
Storage Server Selection Strategy
services.AddFastDFS(options =>
{
options.TrackerServers = new[] { "192.168.1.100:22122" };
// Let the tracker decide (default, most efficient)
options.StorageSelectionStrategy = StorageSelectionStrategy.TrackerSelection;
// Or choose client-side strategy:
// options.StorageSelectionStrategy = StorageSelectionStrategy.RoundRobin;
// options.StorageSelectionStrategy = StorageSelectionStrategy.Random;
// options.StorageSelectionStrategy = StorageSelectionStrategy.FirstAvailable;
});
HTTP URL Generation (for FastDFS Nginx Module)
FastDFS supports HTTP access through the fastdfs-nginx-module. This SDK can generate HTTP URLs for files:
services.AddFastDFS(options =>
{
options.TrackerServers = new[] { "192.168.1.100:22122" };
options.HttpConfig = new HttpConfiguration
{
// Option 1: Configure HTTP server URLs per group
ServerUrls = new Dictionary<string, string>
{
{ "group1", "http://img1.example.com" },
{ "group2", "http://img2.example.com" }
},
// Option 2: Use template with storage server IP
// DefaultServerUrlTemplate = "http://{ip}:8080",
// Anti-steal token (optional, requires Nginx module setup)
AntiStealTokenEnabled = true,
SecretKey = "your-secret-key-here",
DefaultTokenExpireSeconds = 3600
};
});
Generate HTTP URLs:
// Simple HTTP URL
string url = await client.GetFileUrlAsync(fileId);
// Result: http://img1.example.com/group1/M00/00/00/xxxxx.jpg
// With custom download filename
string url = await client.GetFileUrlAsync(fileId, "my-photo.jpg");
// Result: http://img1.example.com/group1/M00/00/00/xxxxx.jpg?attname=my-photo.jpg
// Secure URL with anti-steal token
string secureUrl = await client.GetFileUrlWithTokenAsync(fileId, expireSeconds: 3600);
// Result: http://img1.example.com/group1/M00/00/00/xxxxx.jpg?token=abc123&ts=1234567890
// Secure URL with custom filename
string secureUrl = await client.GetFileUrlWithTokenAsync(fileId, 3600, "photo.jpg");
// Result: http://img1.example.com/group1/M00/00/00/xxxxx.jpg?token=abc123&ts=1234567890&attname=photo.jpg
Logging Integration
services.AddLogging(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Debug);
});
services.AddFastDFS(options =>
{
options.TrackerServers = new[] { "192.168.1.100:22122" };
});
// Logs automatically include:
// - Connection pool events (creation, reuse, disposal)
// - Tracker failover events
// - Upload/download operations
// - Network errors
Error Handling
try
{
var fileId = await client.UploadAsync(null, bytes, "jpg");
}
catch (FastDFSNetworkException ex)
{
// Network-related errors (connection failed, timeout, etc.)
_logger.LogError(ex, "Network error: {Endpoint}", ex.RemoteEndpoint);
}
catch (FastDFSProtocolException ex)
{
// Protocol errors (invalid response, server error, etc.)
_logger.LogError(ex, "Protocol error: {ErrorCode}", ex.ErrorCode);
}
catch (FastDFSException ex)
{
// General FastDFS errors
_logger.LogError(ex, "FastDFS error: {Message}", ex.Message);
}
Multi-Cluster Failover Example
public class RobustFileService
{
private readonly IFastDFSClient _primaryClient;
private readonly IFastDFSClient _backupClient;
private readonly ILogger<RobustFileService> _logger;
public RobustFileService(IFastDFSClientFactory factory, ILogger<RobustFileService> logger)
{
_primaryClient = factory.GetClient("primary");
_backupClient = factory.GetClient("backup");
_logger = logger;
}
public async Task<string> UploadWithFailover(byte[] content, string extension)
{
try
{
return await _primaryClient.UploadAsync(null, content, extension);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Primary cluster failed, switching to backup");
return await _backupClient.UploadAsync(null, content, extension);
}
}
}
Object Lifecycle
DI Scenario
All clients are singletons managed by FastDFSClientFactory. The DI container disposes the factory (and all underlying TCP connections) when the application shuts down. No manual cleanup is needed.
DI Container (Singleton)
└── FastDFSClientFactory ← sole lifecycle owner
└── FastDFSClient ← one per named cluster
├── TrackerClient → ConnectionPool(s) → TCP connections
└── StorageClient → ConnectionPool(s) → TCP connections (per storage node)
Non-DI Scenario
| Entry Point | Lifecycle management |
|---|---|
FastDFSClientBuilder.CreateClient() |
Caller owns the client — use using or call Dispose() explicitly |
FastDFSClientManager |
Manager owns all clients — dispose the manager to release everything |
// FastDFSClientBuilder — caller owns
using var client = FastDFSClientBuilder.CreateClient(options);
// FastDFSClientManager — manager owns
using var manager = new FastDFSClientManager();
manager.AddClient("default", options);
var client = manager.GetClient("default"); // do NOT dispose individually
Performance Tips
- Reuse Clients:
IFastDFSClientinstances are thread-safe and designed for long-term reuse - Tune Pool Size: Adjust
MaxConnectionPerServerto match your workload concurrency - Shared Connection Pool: Multiple named clients with identical configuration automatically share one connection pool — no extra setup needed
- Use Async: All operations are async — always
awaitto avoid thread starvation - Storage Selector:
TrackerSelection(default) is the most efficient strategy; use client-side strategies only when you need custom routing
Building
# Restore dependencies
dotnet restore
# Build
dotnet build
# Run tests
dotnet test
# Pack NuGet package
dotnet pack -c Release
Project Structure
FastDFS.Client/
├── src/
│ ├── FastDFS.Client/ # Core library (zero dependencies)
│ │ ├── Protocol/ # FastDFS binary protocol (requests/responses)
│ │ ├── Connection/ # TCP connection and pooling
│ │ ├── Tracker/ # ITrackerClient — tracker server communication
│ │ ├── Storage/ # IStorageClient — storage server operations
│ │ ├── Configuration/ # Configuration models
│ │ ├── Exceptions/ # Custom exception types
│ │ └── Utilities/ # Helper utilities
│ └── FastDFS.Client.DependencyInjection/ # DI extensions (AddFastDFS, IFastDFSClientFactory)
├── tests/
│ └── FastDFS.Client.Tests/ # Unit tests (86+ tests)
├── benchmarks/
│ └── FastDFS.Client.Benchmarks/ # BenchmarkDotNet performance tests
└── samples/
└── FastDFS.Client.Samples/ # Usage examples
Requirements
- FastDFS Server 6.0+ (recommended)
- .NET Standard 2.0 compatible runtime
- .NET Framework 4.6.1+
- .NET Core 2.0+
- .NET 5.0+
- .NET 6.0+
- .NET 7.0+
- .NET 8.0+
License
MIT License - see LICENSE file for details
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Support
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
- System.Buffers (>= 4.6.1)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on FastDFS.Client:
| Package | Downloads |
|---|---|
|
SharpAbp.Abp.FastDFS
SharpAbp FastDFS Module |
|
|
FastDFS.Client.DependencyInjection
Dependency injection extensions for FastDFS.Client with IOptions pattern and multi-cluster support |
GitHub repositories
This package is not used by any popular GitHub repositories.
Initial release with core functionality:
- FastDFS protocol implementation
- Connection pooling with lifecycle management
- Tracker and Storage client
- Multi-cluster support
- Dependency injection integration
- High-performance Socket-based networking