{"id":23100924,"url":"https://github.com/sergiobarriel/azurestoragewrapper","last_synced_at":"2025-08-16T14:32:12.338Z","repository":{"id":192131023,"uuid":"686057525","full_name":"sergiobarriel/AzureStorageWrapper","owner":"sergiobarriel","description":"Wrapper for Azure Storage, designed to simplify the file upload process and provide links for downloading them. It also supports file deletion and enumeration.","archived":false,"fork":false,"pushed_at":"2025-06-10T15:02:08.000Z","size":342,"stargazers_count":5,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"dev","last_synced_at":"2025-07-12T02:27:26.026Z","etag":null,"topics":["azure-storage"],"latest_commit_sha":null,"homepage":"https://www.nuget.org/packages/AzureStorageWrapper/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sergiobarriel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"ko_fi":"sergiobarriel"}},"created_at":"2023-09-01T16:28:18.000Z","updated_at":"2025-06-10T15:02:13.000Z","dependencies_parsed_at":"2024-02-07T11:49:42.931Z","dependency_job_id":"152615d4-d9ac-4d7a-a28e-3c43f900f805","html_url":"https://github.com/sergiobarriel/AzureStorageWrapper","commit_stats":null,"previous_names":["sergiobarriel/azure.storage.wrapper"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sergiobarriel/AzureStorageWrapper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiobarriel%2FAzureStorageWrapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiobarriel%2FAzureStorageWrapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiobarriel%2FAzureStorageWrapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiobarriel%2FAzureStorageWrapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sergiobarriel","download_url":"https://codeload.github.com/sergiobarriel/AzureStorageWrapper/tar.gz/refs/heads/dev","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiobarriel%2FAzureStorageWrapper/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270723260,"owners_count":24634350,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-16T02:00:11.002Z","response_time":91,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["azure-storage"],"created_at":"2024-12-16T23:49:00.734Z","updated_at":"2025-08-16T14:32:12.321Z","avatar_url":"https://github.com/sergiobarriel.png","language":"C#","funding_links":["https://ko-fi.com/sergiobarriel"],"categories":[],"sub_categories":[],"readme":"![AzureStorageWrapper poster](https://raw.githubusercontent.com/sergiobarriel/AzureStorageWrapper/main/images/poster.png)\n\n# AzureStorageWrapper\n\n**AzureStorageWrapper** it's a wrapper for Azure Storage **blob** service, aimed at simplifying the file upload process and obtaining links for downloading them.\n\n[![run tests](https://github.com/sergiobarriel/AzureStorageWrapper/actions/workflows/run-tests.yml/badge.svg?branch=dev)](https://github.com/sergiobarriel/AzureStorageWrapper/actions/workflows/run-tests.yml) \n[![run tests and deploy](https://github.com/sergiobarriel/AzureStorageWrapper/actions/workflows/run-tests-and-deploy.yml/badge.svg?branch=main)](https://github.com/sergiobarriel/AzureStorageWrapper/actions/workflows/run-tests-and-deploy.yml)\n![downloads](https://img.shields.io/nuget/dt/AzureStorageWrapper)\n\n📦 View package on [NuGet Gallery](https://www.nuget.org/packages/AzureStorageWrapper/)\n📦 View package on [nuget.info](https://nuget.info/packages/AzureStorageWrapper)\n\n## Supported framework\n\n* .Net Standard 2.0\n* .Net Standard 2.1\n\n## Installation the package\nInstall the AzureStorageWrapper client library for .NET with NuGet:\n\n```dotnetcli\ndotnet add package AzureStorageWrapper\n ```\n\n``` nuget\nNuGet Install-Package AzureStorageWrapper -Version x.y.z\n```\nThis 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](https://learn.microsoft.com/es-es/nuget/reference/ps-reference/ps-ref-install-package).\n\n``` \n\u003cPackageReference Include=\"AzureStorageWrapper\" Version=\"x.y.z\" /\u003e\n```\n For projects that support [PackageReference](https://learn.microsoft.com/es-es/nuget/consume-packages/package-references-in-project-files), copy this XML node into the project file to reference the package.\n\n# Usage\n\n## Dependency injection\n\n### File configuration\n#### local.settings.json (Azure Function)\n```json\n{\n    \"IsEncrypted\": false,\n    \"Values\": {\n        \"AzureWebJobsStorage\": \"UseDevelopmentStorage=true\",\n        \"FUNCTIONS_WORKER_RUNTIME\": \"dotnet-isolated\",\n        \n        //Configuration StorageWrapper\n        StorageWrapper_ConnectionsString=\"...\" //Is a configuration setting that contains the necessary information to connect to your Azure Storage account. Type: String. Recomended\n        StorageWrapper_MaxSasUriExpiration=\"...\" //It sets the maximum duration for the Shared Access Signature (SAS) URI of an Azure Storage file. Type: int. Default: 600. Optional\n        StorageWrapper_DefaultSasUriExpiration=\"...\" //It sets the default duration for the Shared Access Signature (SAS) URI of an Azure Storage file. Type: int. Default: 300. Optional\n        StorageWrapper_ReateContainerIfNotExists=\"...\" //Determines whether the Azure Storage container should be automatically created if it does not already exist.Type: bool. Defult: true. Optional\n    }\n}\n```\n#### app.settings.json \n```json\n{\n    //Configuration StorageWrapper\n        //Configuration StorageWrapper\n        StorageWrapper_ConnectionsString=\"...\" //Is a configuration setting that contains the necessary information to connect to your Azure Storage account. Type: String. Recomended\n        StorageWrapper_MaxSasUriExpiration=\"...\" //It sets the maximum duration for the Shared Access Signature (SAS) URI of an Azure Storage file. Type: int. Default: 600. Optional\n        StorageWrapper_DefaultSasUriExpiration=\"...\" //It sets the default duration for the Shared Access Signature (SAS) URI of an Azure Storage file. Type: int. Default: 300. Optional\n        StorageWrapper_ReateContainerIfNotExists=\"...\" //Determines whether the Azure Storage container should be automatically created if it does not already exist.Type: bool. Defult: true. Optional\n}\n```\n\n\u003e **Remember** that the variable names are suggested and you can change them according to your needs.\n\n### Minimal Configuration\nTo add **AzureStorageWrapper** to dependencies container.\n\n```csharp\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAzureStorageWrapper();\n```\n\u003e **Warning:** You must configure the **StorageWrapper_ConnectionString** variable in the configuration file.\n\n### ConnectionString Configuration\nTo add **AzureStorageWrapper** to dependencies container, just use the method `AddAzureStorageWrapper(string connectionstring)`\n\n```csharp\nvar builder = WebApplication.CreateBuilder(args);\nvar connectionString = builder.Configuration[\"Connection_String\"];\nbuilder.Services.AddAzureStorageWrapper(connectionString);\n```\n### Configuration\nTo add **AzureStorageWrapper** to dependencies container, just use the method `AddAzureStorageWrapper(...)`\n\n```csharp\nvar builder = WebApplication.CreateBuilder(args);\nvar connectionString = builder.Configuration[\"Connection_String\"];\nbuilder.Services.AddAzureStorageWrapper(options =\u003e\n{\n    options.ConnectionString = \"azure-storage-connection-string\";\n    options.MaxSasUriExpiration = 600;\n    options.DefaultSasUriExpiration = 300;\n    options.CreateContainerIfNotExists = true;\n});\n```\n\nThese are the *main* properties:\n- **ConnectionString**: The connection string of your Azure Storage Account. You can export it by following [this documentation](https://learn.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal#view-account-access-keys)\n- **MaxSasUriExpiration**: You can set a maximum duration value for the Shared Access Signature (SAS) of an Azure Storage file to prevent someone from attempting to generate a token with a longer expiration time. The duration is expressed in seconds.\n- **DefaultSasUriExpiration**: You can download a file using AzureStorageWrapper without specifying the `ExpiresIn` property. By doing so, this value will be automatically set. The duration is xpressed in seconds\n- **CreateContainerIfNotExists**: When uploading a file to Azure Storage, you need to specify the container, which may not exist and can be created automatically. You can set it to `true` or `false` based on your requirements. Consider this property if you have automated your infrastructure with an Infrastructure as Code (IaC) mechanism because it affects the state of your infrastructure.\n\nThen you can inject `IAzureStorageWrapper` into your services through constructor:\n\n```csharp\npublic class MyService\n{\n    private IAzureStorageWrapper _storageWrapper;\n\n    public MyService(IAzureStorageWrapper storageWrapper)\n    {\n        _storageWrapper = storageWrapper;\n    }\n}\n```\n\n## Upload blobs\n\nThere are **3 options** to upload blobs, all the ways follow the same pattern:\n\n- You need to specify the `Name` and `Extension`.\n- You need to specify the `Container` where you want to store the file.\n- You can add additional `Metadata` with relevant information.\n\nThe file will be placed in `Base64`, `Bytes` or `Stream` property.\n\nOptionally you can specify the `UseVirtualFolder` property to save the file in a virtual folder. By default, it is set to `true`. We delve deeper into this point later.\n\n### Base64\n\n```csharp\nvar base64 = \"SGVsbG8g8J+Zgg==\";\n\nvar command = new UploadBase64()\n{\n    Base64 = base64,\n    Container = \"files\",\n    Name = \"hello\",\n    Extension = \"md\",\n    Metadata = new Dictionary\u003cstring, string\u003e() { {\"key\", \"value\"} }\n};\n\nvar response = await _azureStorageWrapper.UploadBlobAsync(command);\n```\n\n### Byte []\n\n```csharp\nvar bytes = Convert.FromBase64String(\"SGVsbG8g8J+Zgg==\");\n\nvar command = new UploadBytes()\n{\n    Bytes = bytes,\n    Container = \"files\",\n    Name = \"hello\",\n    Extension = \"md\",\n    Metadata = new Dictionary\u003cstring, string\u003e() { {\"key\", \"value\"} }\n};\n\nvar response = await _azureStorageWrapper.UploadBlobAsync(command);\n```\n\n### Stream\n\n```csharp\nvar stream = new MemoryStream(Convert.FromBase64String(\"SGVsbG8g8J+Zgg==\"));\n\nvar command = new UploadStream()\n{\n    Stream = stream,\n    Container = \"files\",\n    Name = \"hello\",\n    Extension = \"md\",\n    Metadata = new Dictionary\u003cstring, string\u003e() { {\"key\", \"value\"} }\n};\n\nvar response = await _azureStorageWrapper.UploadBlobAsync(command);\n```\n\n## Response after upload blobs\n\nRegardless of the chosen upload mechanism, you will always receive a `BlobReference` object after uploading a file.\n\n```csharp\npublic class BlobReference\n{\n    public string Container { get; set; }\n    public string Name { get; set; }\n    public string Extension { get; set; }\n    public string Uri { get; set; }\n    public string SasUri { get; set; }\n    public DateTime SasExpires { get; set; }\n    public IDictionary\u003cstring, string\u003e Metadata { get; set; }\n}\n```\n\nIn example, if you upload the file `hello.md` file to container `files` you will receive a response like:\n\n```json\n{\n    \"Container\": \"files\",\n    \"Name\": \"hello\",\n    \"Extension\": \"md\",\n    \"Uri\": \"https://accountName.blob.core.windows.net/files/5a19306fc5014a4/hello.md\",\n    \"SasUri\": \"https://accountName.blob.core.windows.net/files/5a19306fc5014a4/hello.md?sv=2021-10-04\\u0026se=2023-09-03T16%3A17%3A02Z\\u0026sr=b\\u0026sp=r\\u0026sig=8hs8AzxABevSTc5y%2BhOWDDN%2FH5qFSpA8Omj4uqoxzms%3D\",\n    \"SasExpires\": \"2023-09-03T16:17:02.8220993Z\",\n    \"Metadata\": {\n        \"key\": \"value\",\n        \"_timestamp\": \"03/09/2023 16:11:02\"\n    }\n}\n```\n\n\u003e It is your responsibility to save the reference (URI property) of the file you have uploaded to Azure Storage somewhere, as you will need it for later downloads.\n\n## Virtual Folders\n\nBy default, files are stored in the desired container using **virtual folders**, allowing you to upload files with the same name without risking name collisions.\n\nFor example, a virtual folder with a unique identifier is automatically created between the container name `files` and the file name `hello.md`, resulting in a URI like this:\n\n`https://accountName.blob.core.windows.net/files/5a19306fc5014a4/hello.md`\n\nHowever, you can customize the `UseVirtualFolder` property, which by default has a value of `true` but you can set it to `false` if you wish.\n\n\u003e ⚠️ When `UseVirtualFolder` is set to `false`, files will **NOT** be stored in virtual directories. This change may lead to file name collisions, causing files to be **overwritten**.\n\nIn this scenario, you must implement your own mechanism to generate unique file names.\n\n```csharp\nvar base64 = \"SGVsbG8g8J+Zgg==\";\n\nvar command = new UploadBase64()\n{\n    Base64 = base64,\n    Container = \"files\",\n    Name = $\"{Guid.NewGuid()}\",\n    Extension = \"md\",\n    UseVirtualFolder = false // be careful!\n};\n\nvar response = await _azureStorageWrapper.UploadBlobAsync(command);\n```\n\n## Download blob references\n\nTo download a blob reference, you need to specify the `Uri`, which you should have stored in your system in some way\n\n```csharp\nvar query = new DownloadBlobReference()\n{\n    Uri = \"https://accountName.blob.core.windows.net/files/5a19306fc5014a4/hello.md\"\n    ExpiresIn = 60,\n};\n\nvar response = await _azureStorageWrapper.DownloadBlobReferenceAsync(query);\n```\n\nThe response when *downloading* file reference resembles the response when *uploading* files:\n\n```json\n{\n    \"Container\": \"files\",\n    \"Name\": \"hello\",\n    \"Extension\": \"md\",\n    \"Uri\": \"https://accountName.blob.core.windows.net/files/5a19306fc5014a4/hello.md\",\n    \"SasUri\": \"https://accountName.blob.core.windows.net/files/5a19306fc5014a4/hello.md?sv=2021-10-04\\u0026se=2023-09-03T16%3A17%3A02Z\\u0026sr=b\\u0026sp=r\\u0026sig=8hs8AzxABevSTc5y%2BhOWDDN%2FH5qFSpA8Omj4uqoxzms%3D\",\n    \"SasExpires\": \"2023-09-03T16:17:02.8220993Z\",\n    \"Metadata\": {\n        \"_timestamp\": \"03/09/2023 16:11:02\"\n    }\n}\n```\n\n## Delete blobs\n\nYou can delete a blob by specifying the `Uri`.\n\n```csharp\nvar command = new DeleteBlob()\n{\n    Uri = \"https://accountName.blob.core.windows.net/files/5a19306fc5014a4/hello.md\"\n};\n\nawait _azureStorageWrapper.DeleteBlobAsync(command);\n```\n\n## Enumerate blobs\n\nYou can list all blobs in a container by using the method `EnumerateBlobsAsync`. \n\nThe response `BlobReferenceCollection` will contain a collection of `BlobReference` elements.\n\n### Without pagination\n\nYou should only run this query if you are certain that your container stores a small number of blobs.\n\n```csharp\nvar query = new EnumerateBlobs()\n{\n    Container = \"files\",\n    Paginate = false\n};\n\nvar response = await _azureStorageWrapper.EnumerateAllBlobsAsync(query);\n```\n\n### With pagination\n\n```csharp\nvar query = new EnumerateBlobs()\n{\n    Container = \"files\",\n    Paginate = true.\n    Size = 10,\n};\n\nvar response = await _azureStorageWrapper.EnumerateBlobsAsync(query);\n\n```\nThen you can request additional pages by using the `ContinuationToken` property in the next request.\n\n```csharp\nvar firstQuery = new EnumerateBlobs()\n{\n    Container = \"files\",\n    Paginate = true,\n    Size = 10,\n};\n\nvar firstResponse = await _azureStorageWrapper.EnumerateBlobsAsync(firstQuery);\n\nvar secondQuery = new EnumerateBlobs()\n{\n    Container = \"files\",\n    Paginate = true,\n    Size = 10,\n    ContinuationToken = firstResponse.ContinuationToken\n};\n\nvar secondResponse = await _azureStorageWrapper.EnumerateBlobsAsync(secondQuery);\n```\n\n# Contributors / Collaborators\n\nThese individuals have contributed to the repository through suggestions, error corrections, or by opening issues. Thanks 😊\n\n- [ginodcs](https://github.com/ginodcs)\n- [christian-cell](https://github.com/christian-cell)\n- [scabrera](https://github.com/scabrera)\n\n# Sponsor\n\nIf you like the project, you can consider making a donation at [ko-fi.com](https://ko-fi.com/sergiobarriel)\n\n# Support\n\nYou can contact me via Bluesky [@sergiobarriel.bsky.social](https://bsky.app/profile/sergiobarriel.bsky.social) or Twitter [@sergiobarriel](https://twitter.com/sergiobarriel), or if you have an [issue](https://github.com/sergiobarriel/AzureStorageWrapper/issues), you can open one 🙂\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsergiobarriel%2Fazurestoragewrapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsergiobarriel%2Fazurestoragewrapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsergiobarriel%2Fazurestoragewrapper/lists"}