Repository navigation
Migration Guide
FluentStorage is the successor of Storage.NET, which had many systemic issues that I could not fix before. The community that gathered around FluentStorage added a few features, but the API has stayed in mostly the same shape. The library was also very limited, and only offered basic read/write functionality for storage providers, with a focus on Azure. GCP and S3 were an afterthought, and were implemented in a partial, inconsistant and unsatisfactory manner.
In 2026, the situation has changed. With the world moving onto the cloud, we need a cloud-first approach. We need an API surface that can unify the needs of AWS, Azure, GCP, and all other cloud storage providers, in addition to offering compatibility with disk based systems like FTP and SFTP.
In this release my goals were:
- Redesign the API surface to offer a unified API across all major cloud providers
- Add all of the popular cloud storage providers
- Add all the "missing" APIs to make this library actually functional in complex projects
- Fix broken logic, the broken path system, and the various unjustifiable decisions made by the original author
- Fix technical debt by dropping old providers and outdated concepts
- Modernize the codebase
- Improve code organization
- Improve the automated test suite
Instead of making multiple releases with constant breaking API changes, I decided to roll this into a single large release. v8 is the culmination of all of the above goals.
- All extension-method factory API has been removed: For example
StorageFactory.BlobsandStorageFactory.Messagesno longer exist. - Factory classes have been introduced on a per-provided basis.
- Factory classes can be accessed directly to construct new
IStoreobjects: For exampleStorageFactory.Blobs.AwsS3is nowAwsS3Storage.FromCredentials. - All methods are
asyncand the "Async" suffix has been dropped. The old goal of supporting sync and async makes no sense since all cloud transfer APIs are async and the handful of edge cases like local memory storage and local disk storage do not matter. - All methods have an optional
CancellationTokenrather than forcing users to provide one. - At initialization, S3-compatible stores will no longer create the bucket if the specified bucket does not exist. FluentStorage is not an IaC framework and this "feature" has been troublesome for many cloud storage providers.
-
WriteAsync()is renamed toSetObject()and will auto compute the object's MIME type (Content-Type) if it is not supplied. -
GetPresignedUrl()and its variations will auto compute the object's MIME type (Content-Type). -
CreateDirectory()will no longer create a "dummy file" in a cloud storage bucket. -
RenameAsync()is renamed toMoveObject()and will no longer perform a recursive copy and delete, instead it will efficiently move the object/file from the old path to the new path only if it exists. -
ExistsAsync()is renamed toObjectExists()and will consistently return true/false if the file or folder exists on the bucket/server. -
DeleteAsync()is renamed toDeleteObject()and will consistently delete a single file or folder from the bucket/server. -
DeleteObjects()will consistently delete multiple files or folders from the bucket/server. -
GetClient()will consistently return the internal cloud SDK client for all types of cloud storage. (ReplacesNativeBlobClient) - Collections returned by APIs will always be
Listinstead ofIReadOnlyCollection. -
StoragePath.Normalizehas been completely rewritten to offer a unified path system across all disk and cloud stores - Exceptions are moved into the
FluentStorage.Exceptionsnamespace - Enums are moved into the
FluentStorage.Enumsnamespace - FTP and SFTP server stores now support directory and server API in addition to the object manipulation API
- Bucket stores now support streaming/seeking, presigned URL and more object manipulation API
- We no longer support Databricks, ServiceFabric, EventHub, Virtual storage and ZIP archives
- Add these imports into code that uses FluentStorage:
using FluentStorage.Enums;
using FluentStorage.Exceptions;
using FluentStorage.Model;- Find and replace the most common terms:
| Find | Replace |
|---|---|
| StorageFactory.Blobs.FromConnectionString | StorageFactory.FromConnectionString |
| StorageFactory.Blobs.DirectoryFiles | StorageFactory.Disk |
| StorageFactory.Blobs.AwsS3 | AwsS3Storage.FromCredentials |
| FluentStorage.AWS.Blobs | FluentStorage.AWS.Storage |
| IBlobStorage | IStore |
| IHierarchicalBlobStorage | IStore |
| IExtendedBlobStorage | IStore |
| IAzureBlobStorage | IAzureBlobStore |
| IMessenger | IQueue |
- Switch to the new factory classes instead of using
StorageFactory.Blobs. - Delete the suffix "Async" from all API method calls.
AwsS3StorageAwsSqsStorageBackblazeB2StorageCloudflareR2StorageDigitalOceanSpacesStorageHetznerStorageVultrStorageWasabiStorageAzureBlobStoreAzureDataLakeStorageAzureFilesStorageAzureKeyVaultStorageAzureQueueStorageAzureServiceBusGoogleCloudStorageMinioStorageMinioS3StorageAlibabaStorageFtpStorageSftpStorage
- Server info
GetServer
- Directory manipulation
CreateDirectoryDeleteDirectoryDirectoryExistsMoveDirectoryGetFilePermissionsSetFilePermissions
- File manipulation
MoveObjectDownloadObjectUploadObjectGetBytesSetBytes
- Client info
GetClient
- File seeking/streaming
OpenSeekableOpenRangeGetObjectLength
- File manipulation
MoveObjectDownloadObjectUploadObjectOpenReadOpenWrite
- Presigned URL generation
GetUploadUrlGetDownloadUrl-
GetPresignedUrl(S3 friendly) -
GetObjectSas(Azure friendly)
The entire FluentStorage API surface was redesigned from scratch to keep it meaningful, simple and consistent.
| Old Class | New Class | Notes |
|---|---|---|
| Blob | StoreObject | "Object" is the most common terminology across all providers |
| IBlobStorage | IStore | "Bucket" is the most common terminology across all providers |
| IHierarchicalBlobStorage | IStore | |
| IExtendedBlobStorage | IStore | |
| IAzureBlobStorage | IAzureBlobStore | "Store" terminology |
| IAzureDataLakeStorage | IAzureDataLakeStore | "Store" terminology |
| IMessenger | IQueue | "Queue" is the most common terminology across all providers |
| IMessageReceiver | IQueueReceiver | |
| IMessageProcessor | IQueueProcessor | |
| IAzureServiceBusMessenger | IAzureServiceBus | |
| InMemoryBlobStorage | MemoryStore | For simplicity |
| InMemoryMessenger | MemoryMessenger | For simplicity |
| StorageConnectionString | ConnectionString | For consistency |
| KnownPrefix | ConnectionStringPrefix | For consistency |
| KnownParameter | ConnectionStringParam | For consistency |
| RecursionMode | StorageRecursion | For consistency |
| BlobItemKind | StorageObjectType | For consistency |
| ErrorCode | StorageErrorCode | For consistency |
| ListOptions | StorageListOptions | For consistency |
| Old Static API | New Static API | Notes |
|---|---|---|
| StorageFactory.Modules.UseAwsStorage | AwsStorage.Use | For connection strings |
| StorageFactory.Modules.UseAzureBlobStorage | AzureBlobStorage.Use | For connection strings |
| StorageFactory.Modules.UseAzureFilesStorage | AzureFilesStorage.Use | For connection strings |
| StorageFactory.Modules.UseAzureKeyVault | AzureKeyVaultStorage.Use | For connection strings |
| StorageFactory.Modules.UseAzureQueues | AzureQueueStorage.Use | For connection strings |
| StorageFactory.Modules.UseFtpStorage | FtpStorage.Use | For connection strings |
| StorageFactory.Modules.UseSftpStorage | SftpStorage.Use | For connection strings |
| StorageFactory.Modules.UseGoogleCloudStorage | GoogleCloudStorage.Use | For connection strings |
| StorageFactory.Blobs.AzureBlob* | AzureBlobStorage.From... | |
| StorageFactory.Blobs.AzureFiles* | AzureFilesStorage.From... | |
| StorageFactory.Blobs.AzureDataLake* | AzureDataLakeStorage.From... | |
| StorageFactory.Blobs.AzureKeyVault* | AzureKeyVaultStorage.From... | |
| StorageFactory.Messages.AzureStorageQueue* | AzureQueueStorage.From... | |
| StorageFactory.Messages.AzureServiceBus* | AzureServiceBus.From... | |
| StorageFactory.Blobs.GoogleCloud* | GoogleCloudStorage.From... | |
| StorageFactory.Blobs.AwsS3* | AwsS3Storage.From... | |
| StorageFactory.Messages.AwsSQS* | AwsSqsStorage.From... | |
| StorageFactory.Blobs.MinIO | MinIOStorage.From... | |
| StorageFactory.Blobs.Wasabi | WasabiStorage.From... | |
| StorageFactory.Blobs.DigitalOceanSpaces | DigitalOceanSpacesStorage.From... | |
| StorageFactory.Blobs.BackblazeB2 | BackblazeB2Storage.From... | |
| StorageFactory.Blobs.CloudflareR2 | CloudflareR2Storage.From... | |
| StorageFactory.Blobs.Vultr | VultrStorage.From... | |
| StorageFactory.Blobs.Hetzner | HetznerStorage.From... | |
| StorageFactory.Blobs.Ftp | FtpStorage.From... | |
| StorageFactory.Blobs.Sftp | SftpStorage.From... | |
| StorageFactory.Blobs.FromConnectionString | StorageFactory.FromConnectionString | |
| StorageFactory.Blobs.DirectoryFiles | StorageFactory.Disk | |
| StorageFactory.Blobs.InMemory | StorageFactory.InMemory | |
| StorageFactory.Messages.MessengerFromConnectionString | QueueFactory.FromConnectionString | |
| StorageFactory.Messages.Disk | QueueFactory.Disk | |
| StorageFactory.Messages.InMemory | QueueFactory.InMemory |
| Old Dynamic API | New Dynamic API | Notes |
|---|---|---|
| Blob.Kind | StorageObject.Type | Improved term |
| Blob.CreatedTime | StorageObject.DateCreated | "Time" is a misnomer |
| Blob.LastModificationTime | StorageObject.DateModified | "Time" is a misnomer |
| IBlobStorage.ListAsync | IStore.ListObjects | "Async" removed |
| IBlobStorage.ListDirectoryAsync | IStore.ListDirectory | |
| IBlobStorage.ListFilesAsync | IStore.ListFiles | |
| IBlobStorage.ExistsAsync | IStore.ObjectExists | |
| IBlobStorage.GetBlobAsync | IStore.GetObjectInfo | |
| IBlobStorage.GetBlobsAsync | IStore.GetObjectsInfo | |
| IBlobStorage.SetBlobAsync | IStore.SetObjectInfo | |
| IBlobStorage.SetBlobsAsync | IStore.SetObjectsInfo | |
| IBlobStorage.GetMD5HashAsync | IStore.GetObjectMD5 | |
| IBlobStorage.OpenReadAsync | IStore.OpenRead | |
| IBlobStorage.ReadToStreamAsync | IStore.GetObject | |
| IBlobStorage.ReadBytesAsync | IStore.GetBytes | |
| IBlobStorage.ReadTextAsync | IStore.GetText | |
| IBlobStorage.ReadJsonAsync | IStore.GetJson | |
| IBlobStorage.ReadToFileAsync | IStore.DownloadObject | |
| IBlobStorage.WriteAsync | IStore.SetObject / IStore.SetBytes | |
| IBlobStorage.WriteTextAsync | IStore.SetText | |
| IBlobStorage.WriteJsonAsync | IStore.SetJson | |
| IBlobStorage.WriteFileAsync | IStore.UploadObject | |
| IBlobStorage.CopyToAsync | IStore.CopyObjectToBucket | |
| IBlobStorage.RenameAsync | IStore.RenameObject | |
| IBlobStorage.DeleteAsync | IStore.DeleteObject / IStore.DeleteObjects | |
| IBlobStorage.CreateFolderAsync | IStore.CreateDirectory | |
| IQueue.CreateChannelsAsync | IQueue.CreateChannels | "Async" removed |
| IQueue.ListChannelsAsync | IQueue.ListChannels | |
| IQueue.DeleteChannelsAsync | IQueue.DeleteChannels | |
| IQueue.GetMessageCountAsync | IQueue.GetMessageCount | |
| IQueue.SendAsync | IQueue.SendMessages | |
| IQueue.ReceiveAsync | IQueue.ReceiveMessages | |
| IQueue.PeekAsync | IQueue.PeekMessages | |
| IQueue.DeleteAsync | IQueue.DeleteMessages | |
| IQueue.StartMessageProcessorAsync | IQueue.StartMessageProcessor | |
| IQueue.SendAsync | IQueue.SendMessage | |
| IQueue.CreateChannelAsync | IQueue.CreateChannel | |
| IQueue.DeleteChannelAsync | IQueue.DeleteChannel | |
| IAzureBlobStorage.AcquireLeaseAsync | IAzureBlobStore.AcquireLease | "Async" removed |
| IAzureBlobStorage.BreakLeaseAsync | IAzureBlobStore.BreakLease | |
| IAzureBlobStorage.GetContainerPublicAccessAsync | IAzureBlobStore.GetContainerPublicAccess | |
| IAzureBlobStorage.SetContainerPublicAccessAsync | IAzureBlobStore.SetContainerPublicAccess | |
| IAzureBlobStorage.GetStorageSasAsync | IAzureBlobStore.GetStorageSas | |
| IAzureBlobStorage.GetContainerSasAsync | IAzureBlobStore.GetContainerSas | |
| IAzureBlobStorage.GetBlobSasAsync | IAzureBlobStore.GetBlobSas | |
| IAzureServiceBusMessenger.SendToQueueAsync | IAzureServiceBus.SendToQueue | "Async" removed |
| IAzureServiceBusMessenger.SendToTopicAsync | IAzureServiceBus.SendToTopic | |
| IAzureServiceBusMessenger.SendToSubscriptionAsync | IAzureServiceBus.SendToSubscription | |
| IAzureServiceBusMessenger.CreateQueueAsync | IAzureServiceBus.CreateQueue | |
| IAzureServiceBusMessenger.CreateTopicAsync | IAzureServiceBus.CreateTopic | |
| IAzureServiceBusMessenger.CreateSubScriptionAsync | IAzureServiceBus.CreateSubscription | |
| IAzureServiceBusMessenger.DeleteQueueAsync | IAzureServiceBus.DeleteQueue | |
| IAzureServiceBusMessenger.DeleteSubScriptionAsync | IAzureServiceBus.DeleteSubscription | |
| IAzureServiceBusMessenger.DeleteTopicAsync | IAzureServiceBus.DeleteTopic | |
| IAzureServiceBusMessenger.CountQueueAsync | IAzureServiceBus.CountQueue | |
| IAzureServiceBusMessenger.CountSubScriptionAsync | IAzureServiceBus.CountSubscription | |
| IAzureServiceBusMessenger.CountTopicAsync | IAzureServiceBus.CountTopic | |
| IAzureDataLakeStorage.ListFilesystemsAsync | IAzureDataLakeStore.ListFilesystems | "Async" removed |
| IAzureDataLakeStorage.CreateFilesystemAsync | IAzureDataLakeStore.CreateFilesystem | |
| IAzureDataLakeStorage.DeleteFilesystemAsync | IAzureDataLakeStore.DeleteFilesystem | |
| IAzureDataLakeStorage.SetAccessControlAsync | IAzureDataLakeStore.SetAccessControl | |
| IQueueProcessor.ProcessMessagesAsync | IAzureDataLakeStore.ProcessMessages | |
| IQueueProcessor.ProcessMessagesAsync | IAzureDataLakeStore.ProcessMessages | |
| IQueueReceiver.GetMessageCountAsync | IQueueReceiver.GetMessageCount | "Async" removed |
| IQueueReceiver.ConfirmMessagesAsync | IQueueReceiver.ConfirmMessages | |
| IQueueReceiver.DeadLetterAsync | IQueueReceiver.DeadLetterMessage | |
| IQueueReceiver.PeekMessagesAsync | IQueueReceiver.PeekMessages | |
| IQueueReceiver.StartMessagePumpAsync | IQueueReceiver.StartMessagePump | |
| IQueueReceiver.KeepAliveAsync | IQueueReceiver.KeepAlive |
| Old Namespace | New Namespace |
|---|---|
| FluentStorage.AWS.Blobs | FluentStorage.AWS.Storage |
| FluentStorage.Azure.Blobs.Gen2.Model | FluentStorage.Azure.Blobs.DataLake.Model |
| FluentStorage.Gcp.CloudStorage | FluentStorage.GCP |
We have deleted less used functionality to reduce bloat and help focus on our priority offering.
| Deleted API | Reason |
|---|---|
| StoragePath | Following functions deleted: ComparePath, RemoveRootFolder, GetRootFolder, Rename
|
| ITransaction | Transactions were never implemented so this was dead API |
| EmptyTransaction | Transactions were never implemented so this was dead API |
| VirtualStorage | Virtual storage is out of scope and removed |
| IVirtualStorage | Virtual storage is out of scope and removed |
| StorageFactory.Blobs.Virtual() | Virtual storage is out of scope and removed |
| ZipStore | ZIP Archive support is out of scope and removed |
| StorageFactory.Blobs.ZipFile() | ZIP Archive support is out of scope and removed |
The following libraries have been deprecated and deleted from our codebase, reducing the maintenance effort and helping us focus on the important parts of our offering.
| Package | Reason | |
|---|---|---|
| FluentStorage.Databricks | We will no longer maintain this package because DBFS is not a mainstream storage backend for a storage abstraction library. | |
| FluentStorage.Azure.EventHub | Due to low community usage, we will no longer maintain this library. | |
| FluentStorage.Azure.DataLake | We are no longer maintaining this package as it only caters to DataLake Gen 1, which has been superseded by DataLake Gen2. Gen1 is considered a legacy service and is no longer the direction Microsoft recommends for new development. | |
| FluentStorage.Azure.ServiceFabric | We will no longer maintain this package because ServiceFabric is not a first-class object storage or messaging service, which is outside our current scope, and it also has extremely low community usage. |
Change your NuGet packages and your imports using this mapping:
| Old name | New name |
|---|---|
| Storage.Net | FluentStorage |
| Storage.Net.Amazon.Aws | FluentStorage.AWS |
| Storage.Net.Gcp.CloudStorage | FluentStorage.GCP |
| Storage.Net.Databricks | No longer supported |
| Storage.Net.Ftp | FluentStorage.FTP |
| Storage.Net.Microsoft.Azure.Storage.Blobs | FluentStorage.Azure.Blobs |
| Storage.Net.Microsoft.Azure.Storage.Files | FluentStorage.Azure.Files |
| Storage.Net.Microsoft.Azure.EventHub | No longer supported |
| Storage.Net.Microsoft.Azure.ServiceBus | FluentStorage.Azure.ServiceBus |
| Storage.Net.Microsoft.Azure.KeyVault | FluentStorage.Azure.KeyVault |
| Storage.Net.Microsoft.Azure.ServiceFabric | No longer supported |
| Storage.Net.Microsoft.Azure.Queues | FluentStorage.Azure.Queues |
| Storage.Net.Microsoft.Azure.DataLake.Storage.Gen1 | No longer supported |
- AWS S3 Storage
- Azure Blob Storage
- Azure File Storage
- Azure Data Lake
- Azure Key Vault
- GCP Storage
- Cloudflare R2 Storage
- MinIO Storage
- DigitalOcean Spaces
- Wasabi Storage
- Backblaze B2 Storage
- Hetzner Storage
- Vultr Storage
- MongoDB GridFS Storage
- Alibaba OSS Storage
- FTP Storage
- SFTP Storage
- Git Repository Storage