Introduction
Modern enterprise applications handle millions of documents and attachments: invoices, reports, CAD drawings, images, compliance files, videos, product manuals and more. Storing all attachments in a single storage tier quickly becomes expensive, slow, and difficult to scale.
A realistic system must decide:
Which files should be stored in fast and expensive storage?
Which files can move to cheaper storage after some time?
How do we fetch the correct file tier automatically?
How do we track storage transitions while maintaining auditability?
How do we expose this functionality cleanly in Angular and .NET?
This article describes a complete, production-grade multi-tier attachment storage system with:
Hot storage (local disk, Azure Blob Hot Tier, AWS S3 Standard)
Warm storage (S3 Infrequent Access, Azure Cool Tier, cheaper NAS)
Cold storage (Archive storage, Glacier, compressed & offline-ready files)
The system is metadata-driven, auto-migrates attachments based on rules, integrates with Angular UI for uploads/previews, and exposes a clean .NET API.
This is a senior-level article written in simple Indian English.
System goals
Reduce storage cost without affecting UX.
Automatically move inactivity-based attachments from hot → warm → cold.
Provide seamless access from Angular, regardless of tier.
Add compression, encryption and checksum validation for cold tier.
Provide versioning support, audit logs and access tracing.
Support multi-cloud: Azure, AWS, GCP, on-premises.
Load tier configuration from database.
Make it extensible for future tiers.
Overall Architecture
The architecture has four major layers:
Angular client
.NET Attachment Service API
Storage Processor (background worker or hosted service)
Multi-tier storage backend (hot, warm, cold)
Angular → .NET API → Storage Logic → Hot/Warm/Cold Storage
Workflow Diagram (High-Level Architecture)
+----------------------------+
| Angular App |
| Upload, Preview, Download |
+-------------+--------------+
|
v
+----------------------------+
| .NET API Layer |
| Validate → Route → Store |
+-------------+--------------+
|
+----------------+-----------------+
| |
v v
+--------------------+ +---------------------+
| Metadata DB | <----------> | Background Processor|
| Tier, Status, Logs | | Migration Scheduler |
+--------------------+ +---------------------+
|
v
+------------------+--------------------+------------------+
| Hot | Warm | Cold |
| Fast Storage | Medium Cost | Archive Storage |
+------------------+--------------------+------------------+
Flowchart: Upload to Fetch Flow
+---------------------+
| User uploads file |
+----------+----------+
|
v
+---------------------+
| .NET API validates |
+----------+----------+
|
v
+-------------------------+
| Decide tier = Hot Tier |
+------------+------------+
|
v
+-----------------------+
| Store file in Hot |
| Insert metadata in DB |
+-----------+-----------+
|
v
+-----------------------+
| Angular fetches URL |
| Preview/Download |
+-----------------------+
System Design
Storage tiers
Hot tier
Examples
Azure Blob Hot
S3 Standard
Local SSD
Warm tier
Examples:
Azure Cool
S3 Infrequent Access
Cheaper NAS
Cold tier
Examples:
Database Schema
Attachment
(
AttachmentId BIGINT PK,
FileName NVARCHAR(255),
ContentType NVARCHAR(100),
SizeInBytes BIGINT,
CurrentTier VARCHAR(10), -- HOT, WARM, COLD
Version INT,
UploadDate DATETIME,
LastAccessed DATETIME,
ModifiedDate DATETIME,
StoragePath NVARCHAR(MAX),
Checksum CHAR(64),
EncryptionKeyId INT,
Metadata JSON
)
StorageTierConfig
(
TierName VARCHAR(10),
WarmThresholdDays INT,
ColdThresholdDays INT,
MigrationEnabled BIT
)
AttachmentAccessLog
(
LogId BIGINT,
AttachmentId BIGINT,
AccessedDate DATETIME,
UserId BIGINT
)
.NET Backend (API Layer)
Controller Structure
/api/attachments
POST /upload
GET /{id}
GET /{id}/download
GET /{id}/metadata
Upload flow
Validate file size and type.
Generate checksum (SHA256).
Store in Hot tier.
Insert metadata in DB.
Download flow
Resolve tier from DB.
If cold-tier and requires restoration:
Trigger restore workflow
Return 202 Accepted
Otherwise return file stream or signed URL.
C#: Upload API (Simplified)
[HttpPost("upload")]
public async Task<IActionResult> Upload(IFormFile file)
{
if (file == null || file.Length == 0)
return BadRequest("Invalid file");
var checksum = _checksumService.ComputeSHA256(file.OpenReadStream());
var storagePath = await _hotStorage.SaveAsync(file.FileName, file.OpenReadStream());
var attachment = new Attachment
{
FileName = file.FileName,
ContentType = file.ContentType,
SizeInBytes = file.Length,
CurrentTier = "HOT",
UploadDate = DateTime.UtcNow,
LastAccessed = DateTime.UtcNow,
StoragePath = storagePath,
Version = 1,
Checksum = checksum
};
var id = await _repo.AddAsync(attachment);
return Ok(new { attachmentId = id });
}
C#: Fetch Logic with Tier Routing
[HttpGet("{id}/download")]
public async Task<IActionResult> Download(long id)
{
var attachment = await _repo.Get(id);
switch (attachment.CurrentTier)
{
case "HOT":
return File(await _hotStorage.GetAsync(attachment.StoragePath),
attachment.ContentType,
attachment.FileName);
case "WARM":
return File(await _warmStorage.GetAsync(attachment.StoragePath),
attachment.ContentType,
attachment.FileName);
case "COLD":
bool ready = await _coldStorage.IsRestoredAsync(attachment.StoragePath);
if (!ready)
{
await _restoreQueue.Enqueue(id);
return Accepted(new { message = "Restoration triggered" });
}
return File(await _coldStorage.GetRestoredAsync(attachment.StoragePath),
attachment.ContentType,
attachment.FileName);
default:
return NotFound();
}
}
Background Migration Processor
A hosted service scans all attachments daily:
Check last accessed date.
If > WarmThresholdDays → move to warm tier.
If > ColdThresholdDays → compress, encrypt, move to cold.
Update metadata in DB.
Migration Pseudocode
foreach (attachment in Attachments)
if daysSinceLastAccess > ColdThreshold
MoveToCold(attachment)
else if daysSinceLastAccess > WarmThreshold
MoveToWarm(attachment)
Angular Front-End Implementation
Upload Component
uploadFile(event: any) {
const file = event.target.files[0];
const formData = new FormData();
formData.append('file', file);
this.http.post('/api/attachments/upload', formData)
.subscribe(res => this.loadList());
}
Preview Component
Automatically handles tier-based access using the API.
download(id: number) {
this.http.get(`/api/attachments/${id}/download`, {
responseType: 'blob'
}).subscribe(blob => {
const url = window.URL.createObjectURL(blob);
window.open(url);
});
}
Real-World Best Practices
1. Use signed URLs
Do not expose raw storage paths to Angular.
2. Introduce throttling on large restores
Cold-tier restores must be queued.
3. Store checksum for integrity
Must verify data corruption when restoring.
4. Allow user-driven restore requests
Users may need old documents urgently.
5. Version attachments
Never overwrite existing versions.
6. Add deletion retention periods
Accidental deletions → soft delete → permanent delete.
7. Encrypt cold-tier files
Since cold storage is long-term.
8. Store metadata in JSON
Easier to add new fields without schema changes.
9. Keep migration logs
Needed for audits and compliance.
End-to-End Walkthrough Example
Uploading a new PDF
Angular uploads file
API stores it in hot tier
DB stores metadata
User opens file next day
After 180 days (no access)
After 365 days (no access)
File compressed
Encrypted
Moved to cold
User wants file after 2 years
Security Considerations
1. Encryption keys stored in KMS
Never store keys in app config.
2. Always use HTTPS
Storage URLs must not be exposed over HTTP.
3. Audit logs for access
Track every preview and download.
4. Authentication required
JWT or Azure AD recommended.
5. Virus scanning
Mandatory for uploads.
Scalability
Horizontal scale
Async restore queue
Cold storage restore batching
Conclusion
A multi-tier attachment storage system is not a simple file upload tool. It is a complete architecture that must balance performance, cost, security, and long-term data retention.
This article provided a detailed, production-level solution integrating:
You now have a blueprint to build a scalable, enterprise-level attachment handling system used in ERPs, CRMs, DMS products, aviation systems, and fintech platforms.