Skip to main content

binary-file-storage

Pattern for storing and serving binary files (images, etc.) across both File and Blob storage backends

Informações da origem

Repositório
bradygaster/squad-places
Última atividade na origem
6 de março de 2026 às 18:22
Idioma detectado do SKILL.md
inglês
Estrelas
8
Forks
4

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
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
## Context 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. ## Patterns ### Storage Interface 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. ### FileStorageService - Store binary at `{basePath}/{type}/{ownerId}/{id}{extension}` - Store content type in a `.meta` sidecar file at `{basePath}/{type}/{ownerId}/{id}.meta` - Map content types to extensions in a switch expression - Create the top-level directory in `InitializeAsync()`; owner subdirectories created on-demand via `Directory.CreateDirectory()` ### BlobStorageService - Use a dedicated container (e.g., `images`) - Blob path: `{ownerId}/{id}{extension}` — virtual folder structure - Set `BlobHttpHeaders.ContentType` on upload - For retrieval, search by prefix with `GetBlobsAsync(BlobTraits, BlobStates, prefix, CancellationToken)` - Create container in `InitializeAsync()` ### API Pattern - `POST /api/{type}` — accept base64 body with owner ID, validate owner exists, store, return relative URL - `GET /api/{type}/{ownerId}/{id}` — serve with `Results.File(data, contentType)` - Inline upload on parent entity (e.g., `ImageData` on `PublishArtifactRequest`) as convenience - **Only relative URLs** — reject absolute http/https URLs to prevent SSRF and ensure all content is hosted ### URL Security - Validate URLs with regex matching `/api/{type}/{guid}/{guid}` format - In HTML sanitizer, use `FilterUrl` event to strip non-relative `src` attributes rather than removing the tag entirely - This is more surgical than scheme-based allowlisting — permits relative paths without custom schemes ### Dockerfile Add the new data directory to the `mkdir -p` line. ## Examples See `src/SquadPlaces.Data/FileStorageService.cs` (SaveImageAsync/GetImageAsync) and `src/SquadPlaces.Web/Api/ApiEndpoints.cs` (image endpoints). ## Anti-Patterns - Don't store binary data as base64 in JSON — decode to bytes first - Don't use `GetBlobsAsync(prefix:)` named parameter — the .NET 10 API requires positional args - Don't forget the `.meta` sidecar in file storage — without it, you can't serve with correct Content-Type
Ver no GitHub