Skip to content

Commit 75946c3

Browse files
committed
new post
1 parent 4b95722 commit 75946c3

File tree

4 files changed

+117
-1
lines changed

4 files changed

+117
-1
lines changed

public/assets/cloud-agnostic-2.png

164 KB
Loading

public/assets/cloud-agnostic-3.png

396 KB
Loading

src/content/blog/2025/04-06-dotnet-containered-3.mdx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ date: 2025-04-06 21:30:00
66
topic: "azure-ahead"
77
---
88

9-
<TopicToc topicId="azure-ahead" active="Cloud Agnostic - introducing the AheadDockerized .NET Solution" />
9+
<TopicToc
10+
title="Cloud agnostic series"
11+
topicId="azure-ahead"
12+
active="Cloud Agnostic - introducing the AheadDockerized .NET Solution"
13+
/>
1014

1115
Let's quickly revisit where we're coming from—and where we’re headed:
1216

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---
2+
title: "Cloud Agnostic - Storing & serving files"
3+
slug: cloud-agnostic-2
4+
tags: [software-development, dotnet, web, azure]
5+
date: 2025-04-13 21:30:00
6+
topic: "azure-ahead"
7+
---
8+
9+
<TopicToc
10+
title="Cloud agnostic series"
11+
topicId="azure-ahead"
12+
active="Cloud Agnostic - Storing & serving files"
13+
closed
14+
/>
15+
16+
Let us start with a relatively simple, ubiquitous but necessary service: _Storage of files_. In ahead these are
17+
needed for attachments, images, videos and the like.
18+
19+
```mermaid
20+
graph LR
21+
User((User)) -->|views| Storage
22+
Ahead.Web -->|uploads| Storage
23+
```
24+
25+
After investigating a bit, it appears that the safest bet here is to use something that is **Amazon S3**-compatible.
26+
AWS, by being first here, essentially set the standard for cloud storage, and many companies will offer such
27+
"S3-compatible" storage for you to use. [Here's an example from Switzerland][1].
28+
29+
Granted, using Azure Blob Storage is straightforward from a developer perspective: Having a local "emulator"
30+
is as easy as saying `npm install azurite`. I could imagine that there might be a similar
31+
offering for S3-compatible storage, but considering that I have an Aspire-project available,
32+
setting up a container is also a straightforward activity. The company [min.io][2] offers an "object store"
33+
that can be run at hyperscale as well as locally on a Docker container. The relevant code is [hosted at github][3]
34+
35+
Defining such a container as a resource in Aspire looks like this:
36+
37+
<GHEmbed showHint repo="ahead-dockerized" branch="snapshot_1" file="AppHost/Program.cs" start={10} end={19} />
38+
39+
People who know docker containers probably recognize the different settings that are being set:
40+
41+
* 2 environment variables that define the admin user with which we can log in at min.io's dashboard (port 9090)
42+
* port bindings
43+
* volume bindings
44+
* startup arguments
45+
46+
Aspire gives you a fluent API to define settings on the different kind of resources. Interesting are also the
47+
two variables `minioUser`, `minioPassword` which are defined further up:
48+
49+
<GHEmbed repo="ahead-dockerized" branch="snapshot_1" file="AppHost/Program.cs" start={7} end={8} />
50+
51+
During development, these values come from a "Parameters" section in `appSettings.Development.json`.
52+
Once I start the solution by running the `AppHost` Aspire project, I can access the min.io dashboard by
53+
clicking the provided link in the Aspire dashboard:
54+
55+
![Aspire Dashboard showing the known resources](/assets/cloud-agnostic-2.png)
56+
<figcaption>Aspire Dashboard showing the known resources</figcaption>
57+
58+
In the dashboard I can log in with the provided user / password values and create an access key:
59+
60+
![min.io console that allows to create access keys](/assets/cloud-agnostic-3.png)
61+
<figcaption>min.io console that allows to create access keys</figcaption>
62+
63+
These will be required when accessing the service.
64+
65+
<GHEmbed repo="ahead-dockerized" branch="snapshot_1" file="Ahead.Web/Infrastructure/IBlobStorage.cs" start={77} end={81} />
66+
67+
The relevant Nuget to access the service is simply called **minio**, and provides an up-to-date library to talk to
68+
min.io (and by extension S3 compatible) services.
69+
70+
The `Ahead.Dockerized` solution introduces a blob storage interface with some example API that is linked to UI in order to be able to use it:
71+
72+
<GHEmbed repo="ahead-dockerized" branch="snapshot_1" file="Ahead.Web/Infrastructure/IBlobStorage.cs" start={9} end={14} />
73+
74+
<Info>
75+
This example solution does not implement
76+
multi-tenancy as this is mostly an application aspect independent of the used resources and is therefore not in the focus of this solution.
77+
E.g. `bucketName` would be derived from the tenant used in the current scope.
78+
</Info>
79+
80+
⬇️ This is the implementation of the upload – it takes an `IFormFile` instance and stores it in the service.
81+
I found it amusing that, just coming from an Aspire frontend, I ended up using a fluent interface _again_.
82+
While they're kind of nice to read, getting there is not always as obvious as one cannot see from the API what is
83+
mandatory vs what is not, hence it is sometimes something of a trial and error to get the right incantation.
84+
85+
<GHEmbed repo="ahead-dockerized" branch="snapshot_1" file="Ahead.Web/Infrastructure/IBlobStorage.cs" start={31} end={44} />
86+
87+
Another thing I wanted to check out is the signed url functionality - Links to files in ahead are usually written
88+
like `api/files/{fileId}` - the server then redirects to a signed url, which lets you see the file for a limited amount of time:
89+
90+
```mermaid
91+
sequenceDiagram
92+
participant A as User
93+
participant B as Ahead.Web
94+
participant C as Storage
95+
96+
A->>B: request file with fileId
97+
B->>C: get pre-signed URL
98+
C-->>B: return pre-signed URL
99+
B-->>A: redirect to pre-signed URL
100+
```
101+
102+
This is how to get the necessary url through the min.io API:
103+
104+
<GHEmbed repo="ahead-dockerized" branch="snapshot_1" file="Ahead.Web/Infrastructure/IBlobStorage.cs" start={46} end={55} />
105+
106+
In this case, the link expires after one hour.
107+
108+
All in all, blob storage is, as expected, one of the easier things to treat in a cloud-agnostic way.
109+
110+
[1]: https://flow.swiss/object-storage
111+
[2]: https://min.io
112+
[3]: https://github.com/minio/

0 commit comments

Comments
 (0)