-
-
Notifications
You must be signed in to change notification settings - Fork 73
Migration Guide
FluentStorage is the successor of Storage.NET, which had many systemic design and architectural issues since I took over the project in 2021. The community that gathered around FluentStorage added a few features over the years, 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, inconsistent and unsatisfactory manner.
In 2026, the scenario has changed. With the world moving onto the cloud, we need a cloud-first approach. We also 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 and the broken path system
- Fix technical debt by dropping old providers and outdated concepts
- Modernize the codebase
- Improve code organization
- Improve automated tests
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.
-
New factory API
- 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 extension-method factory API has been removed: For example
-
Redesigned API
- All methods are
asyncand the "Async" suffix has been dropped. - All methods have an optional
CancellationTokenrather than forcing users to provide one. - Collections returned by APIs will always be
Listinstead ofIReadOnlyCollection. -
StoragePathhas been completely rewritten to offer a unified path system across all disk and cloud stores -
GetClient()now consistently returns the internal cloud SDK client (ReplacesNativeBlobClient).
- All methods are
-
Behaviour changes in core API
- 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. -
DeleteObjectwill no longer delete objects from a virtual directory, useDeleteDirectory()for that. -
GetObjectMD5is removed and replaced withGetObjectChecksumthat allows many more hashing algorithms.
-
New providers added
- MongoDB GridFS using native MongoDB Driver
- MinIO using native Minio SDK
- Alibaba OSS using native Aliyun SDK
- Cloudflare R2 using S3-compatible SDK
- Backblaze B2 using S3-compatible SDK
- Hetzner using S3-compatible SDK
- Vultr using S3-compatible SDK
-
New features added
- Bucket stores now support streaming/seeking, presigned URL, object versions, tagging, storage tiers and more
- FTP and SFTP server stores now support directory and server API
-
Outdated providers removed
- We no longer support Databricks, ServiceFabric, EventHub, Virtual storage and ZIP archives
-
Improved automated tests
- Add YAML based configuration for automated test suite
- Merge all tests into a single project, cleanly seperating unit and integration tests
- New tests for path normalization, path combination and path splitting
- New tests for
SeekableStreamused in seeking/streaming - New tests for
IStoreoperations and fixes made to local disk implementation
-
Code organization
- Exceptions are moved into the
FluentStorage.Exceptionsnamespace - Enums are moved into the
FluentStorage.Enumsnamespace - Model objects are moved into the
FluentStorage.Modelnamespace - Data sinks are moved into the
FluentStorage.Sinksnamespace - Streams are moved into the
FluentStorage.Streamingnamespace
- Exceptions are moved into the
- 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
CreateDirectoryDeleteDirectoryDirectoryExistsMoveDirectoryUploadDirectoryDownloadDirectory
- File manipulation
MoveObjectDownloadObjectUploadObjectGetBytesSetBytesGetFilePermissionsSetFilePermissions
- File hashing
GetObjectChecksum
- Client info
GetClient
- File seeking/streaming
OpenSeekableOpenRangeGetObjectLength
- File manipulation
MoveObjectDownloadObjectUploadObjectOpenReadOpenWrite
- Directory manipulation
-
CreateDirectory(NOP) DeleteDirectoryDirectoryExistsUploadDirectoryDownloadDirectory
-
- Presigned URL generation
GetUploadUrlGetDownloadUrl-
GetPresignedUrl(S3 friendly) -
GetObjectSas(Azure friendly)
- Object versioning
IsVersionedListObjectVersionsGetObjectVersionRestoreObjectVersionDeleteObjectVersion
- Object tagging
IsTaggedGetObjectTagsSetObjectTagsDeleteObjectTags
- Object storage tiers
IsTieredGetObjectTierSetObjectTier
- Object hashing
GetObjectChecksum
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.GetObjectChecksum(..., StorageHash.MD5) | |
| 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 |