Introduction
In many enterprise web applications—like maintenance systems, quality control apps, or internal portals—users need to view documents (PDF, DOCX, etc.) even when internet connectivity is lost.
Offline document preview enables users to access and read files that were previously downloaded, without reconnecting to the server.
In this article, we’ll explore how to implement an offline document preview system using Angular (frontend) and ASP.NET Core (backend) with a focus on PDF and DOCX files.
Why Offline Preview Matters
Offline functionality improves user experience and reliability, especially in scenarios like:
Remote sites with weak or unstable networks.
Mobile users accessing internal portals.
Industries like manufacturing, aviation, or field inspection where documents must be available 24×7.
With proper caching and synchronization, users can view their required documents even without a live connection, and the system updates automatically when online again.
Key Technologies Used
| Layer | Technology | Purpose |
|---|
| Frontend | Angular 17 | SPA for document preview |
| Backend | ASP.NET Core 8 Web API | File API and metadata management |
| Storage | IndexedDB (via Service Worker) | Local caching of files |
| File Formats | PDF, DOCX | Supported document types |
| Library | PDF.js / Docx Previewer | Rendering documents in browser |
High-Level Architecture
+-------------------+ +--------------------------+| Angular Frontend | <---> | ASP.NET Core File API ||-------------------| |--------------------------|| Service Worker | | File Storage / DB || IndexedDB Cache | | API Controller || PDF/DOCX Viewer | | Authentication Filter |+-------------------+ +--------------------------+
↑
| (Offline access via cached data)
↓
+-----------------------------+| User Browser (Cache + UI) |+-----------------------------+
Technical Workflow (Flowchart)
┌───────────────────────┐
│ User Requests File │
└──────────┬────────────┘
│
▼
┌────────────────────────┐
│ Check IndexedDB Cache │
└──────────┬─────────────┘
Cache Hit │ Cache Miss
▼
┌────────────────────────┐
│ Load from Cache Offline│
└────────────────────────┘
│
▼
┌────────────────────────┐
│ Fetch from API (Online)│
└──────────┬─────────────┘
│
▼
┌────────────────────────┐
│ Store in IndexedDB │
└──────────┬─────────────┘
│
▼
┌────────────────────────┐
│ Render PDF/DOCX Viewer │
└────────────────────────┘
Step 1: Setting Up the ASP.NET Core Backend
Let’s start by building the backend API for serving document files securely.
1.1. Create the API Controller
[ApiController]
[Route("api/[controller]")]
public class DocumentController : ControllerBase
{
private readonly IWebHostEnvironment _env;
public DocumentController(IWebHostEnvironment env)
{
_env = env;
}
[HttpGet("{fileName}")]
public IActionResult GetDocument(string fileName)
{
var filePath = Path.Combine(_env.ContentRootPath, "Documents", fileName);
if (!System.IO.File.Exists(filePath))
return NotFound("File not found.");
var contentType = GetContentType(filePath);
var bytes = System.IO.File.ReadAllBytes(filePath);
return File(bytes, contentType, fileName);
}
private string GetContentType(string path)
{
var ext = Path.GetExtension(path).ToLowerInvariant();
return ext switch
{
".pdf" => "application/pdf",
".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
_ => "application/octet-stream",
};
}
}
1.2. Folder Structure
/Documents
├── report1.pdf
├── inspection.docx
└── manual.pdf
This API serves the requested file bytes securely to the frontend.
Step 2: Angular Service for File Access
Create a service that fetches documents from the API and manages offline caching using IndexedDB.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({ providedIn: 'root' })
export class DocumentService {
private baseUrl = 'https://localhost:5001/api/document';
constructor(private http: HttpClient) {}
async getDocument(fileName: string): Promise<Blob> {
const cached = await this.getFromCache(fileName);
if (cached) return cached;
const blob = await this.http.get(`${this.baseUrl}/${fileName}`, { responseType: 'blob' }).toPromise();
await this.saveToCache(fileName, blob);
return blob!;
}
private async getFromCache(key: string): Promise<Blob | null> {
const db = await this.openDB();
return (await db.get('files', key)) || null;
}
private async saveToCache(key: string, value: Blob) {
const db = await this.openDB();
await db.put('files', value, key);
}
private async openDB(): Promise<any> {
return new Promise((resolve, reject) => {
const request = indexedDB.open('DocumentCache', 1);
request.onupgradeneeded = (event: any) => {
event.target.result.createObjectStore('files');
};
request.onsuccess = (event: any) => resolve(event.target.result);
request.onerror = reject;
});
}
}
Step 3: Preview Component in Angular
3.1. HTML Template
<div class="preview-container">
<ng-container *ngIf="isPdf; else wordPreview">
<iframe [src]="fileUrl | safeUrl" width="100%" height="600px"></iframe>
</ng-container>
<ng-template #wordPreview>
<iframe [src]="fileUrl | safeUrl" width="100%" height="600px"></iframe>
</ng-template>
</div>
3.2. Component Logic
import { Component, OnInit } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { DocumentService } from './document.service';
@Component({
selector: 'app-document-preview',
templateUrl: './document-preview.component.html',
})
export class DocumentPreviewComponent implements OnInit {
fileUrl: any;
isPdf = true;
fileName = 'report1.pdf';
constructor(private docService: DocumentService, private sanitizer: DomSanitizer) {}
async ngOnInit() {
const blob = await this.docService.getDocument(this.fileName);
this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl(URL.createObjectURL(blob));
this.isPdf = this.fileName.endsWith('.pdf');
}
}
Step 4: Making it Work Offline (Service Worker)
Use Angular’s built-in PWA support for service workers.
4.1. Enable PWA
Run
ng add @angular/pwa
This generates:
4.2. Configure Caching in ngsw-config.json
{"assetGroups": [
{
"name": "documents",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": ["/assets/documents/*.pdf", "/assets/documents/*.docx"]
}
}]}
Step 5: Handling DOCX Rendering
While PDF can be displayed directly, DOCX files can be rendered using an embedded viewer like Office Viewer or converted to PDF server-side.
Example for online rendering
<iframe [src]="'https://view.officeapps.live.com/op/embed.aspx?src=' + documentUrl"
width="100%" height="600px"></iframe>
Step 6: Real-World Example — Maintenance Portal
Imagine a Maintenance Portal used by technicians at remote locations:
When online, files (manuals, reports) are fetched from the server and cached locally.
When offline, cached files are loaded instantly from IndexedDB.
Upon reconnection, updated versions are downloaded silently in the background.
Best Practices
Compress large PDFs before caching.
Implement versioning — store file metadata to check if newer versions are available.
Clean cache periodically to avoid IndexedDB bloating.
Secure API endpoints with JWT authentication.
Use lazy loading for large files.
Performance Optimization
Use PDF.js for smooth rendering and zoom support.
Use Blob streaming in ASP.NET Core for large files instead of reading full bytes into memory.
Prefer lazy caching — store files only when accessed.
Summary
By combining Angular’s PWA capabilities, IndexedDB caching, and ASP.NET Core APIs, you can provide a robust offline document preview system that supports PDF and DOCX files seamlessly.
This solution enhances user experience, reliability, and productivity — making it ideal for enterprise-grade web applications that must function in both connected and disconnected environments.