بنقرة واحدة
binary-file-storage
// Pattern for storing and serving binary files (images, etc.) across both File and Blob storage backends
// Pattern for storing and serving binary files (images, etc.) across both File and Blob storage backends
| name | binary-file-storage |
| description | Pattern for storing and serving binary files (images, etc.) across both File and Blob storage backends |
| domain | storage, api-design |
| confidence | high |
| source | earned — image support feature implementation |
When adding binary file support (images, attachments) to Squad Places, both FileStorageService and BlobStorageService must be extended consistently. The pattern applies to any new binary content type.
Add Save{Type}Async(Guid ownerId, Guid id, byte[] data, string contentType) and Get{Type}Async(Guid ownerId, Guid id) returning (byte[] Data, string ContentType)? to IBlobStorageService. Owner-scoped IDs enable natural data isolation.
{basePath}/{type}/{ownerId}/{id}{extension}.meta sidecar file at {basePath}/{type}/{ownerId}/{id}.metaInitializeAsync(); owner subdirectories created on-demand via Directory.CreateDirectory()images){ownerId}/{id}{extension} — virtual folder structureBlobHttpHeaders.ContentType on uploadGetBlobsAsync(BlobTraits, BlobStates, prefix, CancellationToken)InitializeAsync()POST /api/{type} — accept base64 body with owner ID, validate owner exists, store, return relative URLGET /api/{type}/{ownerId}/{id} — serve with Results.File(data, contentType)ImageData on PublishArtifactRequest) as convenience/api/{type}/{guid}/{guid} formatFilterUrl event to strip non-relative src attributes rather than removing the tag entirelyAdd the new data directory to the mkdir -p line.
See src/SquadPlaces.Data/FileStorageService.cs (SaveImageAsync/GetImageAsync) and src/SquadPlaces.Web/Api/ApiEndpoints.cs (image endpoints).
GetBlobsAsync(prefix:) named parameter — the .NET 10 API requires positional args.meta sidecar in file storage — without it, you can't serve with correct Content-Type