File Download
Introduction
Section titled “Introduction”File download enables users to export data, download reports, save documents, and retrieve generated files from the server.
Why do we need this?
Section titled “Why do we need this?”Modern applications need to download various file types — PDFs, CSVs, images, ZIP archives — with progress indication and proper error handling.
Download Flow
Section titled “Download Flow”sequenceDiagram participant User as User participant UI as Angular Component participant Svc as DownloadService participant API as Server API
User->>UI: Click "Download" button UI->>Svc: download(url, filename) Svc->>API: GET /api/download (responseType: blob) activate API API-->>Svc: DownloadProgress event Svc-->>UI: Update progress % UI-->>User: Show download progress API-->>Svc: Blob data (complete) deactivate API Svc->>Svc: Create ObjectURL Svc->>Svc: Trigger browser download UI-->>User: File saved ✅ Note over Svc: URL.revokeObjectURL()flowchart LR A["User clicksdownload"] --> B["GET requestresponseType: 'blob'reportProgress: true"] B --> C["Receive blobfrom server"] C --> D["CreateURL.createObjectURL()"] D --> E["Create & click<a> element"] E --> F["RevokeObjectURL"] F --> G["Downloadcomplete ✅"]flowchart TD subgraph Options["⚙️ Download Config"] A["responseType: 'blob'"] B["reportProgress: true"] C["observe: 'events'"] end subgraph Events["📡 Event Types"] D["DownloadProgressloaded / total"] E["Responsefinal Blob"] end subgraph Save["💾 Save to Disk"] F["URL.createObjectURL()"] G["Temporary <a> element"] H["Programmatic click()"] I["URL.revokeObjectURL()"] end Options --> Events Events --> SaveBasic File Download
Section titled “Basic File Download”Download as Blob
Section titled “Download as Blob”@Injectable({ providedIn: 'root' })export class FileDownloadService { constructor(private http: HttpClient) {}
download(url: string, filename: string): Observable<HttpEvent<Blob>> { return this.http.get(url, { responseType: 'blob', reportProgress: true, observe: 'events', }).pipe(tap(event => { if (event.type === HttpEventType.DownloadProgress) { const progress = Math.round(100 * event.loaded / event.total!); console.log(`Download progress: ${progress}%`); } })); }
saveBlob(blob: Blob, filename: string) { const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = filename; link.click(); URL.revokeObjectURL(link.href); // clean up memory }}Component Usage
Section titled “Component Usage”@Component({ ... })export class ExportComponent { downloading = signal(false);
downloadReport() { this.downloading.set(true); this.fileService.download('/api/reports/summary', 'summary.pdf') .pipe(finalize(() => this.downloading.set(false))) .subscribe({ next: (event) => { if (event.type === HttpEventType.Response) { this.fileService.saveBlob(event.body!, 'summary.pdf'); } }, error: (err) => console.error('Download failed', err), }); }}Download with Progress
Section titled “Download with Progress”<button (click)="downloadReport()" [disabled]="downloading()"> @if (downloading()) { <span>Downloading...</span> } @else { <span>Download Report</span> }</button>Streaming Large Files
Section titled “Streaming Large Files”For very large files, use responseType: 'blob' with chunked transfer encoding on the server side. Consider using Service Workers to cache partial downloads on unreliable connections.
PDF and Binary Downloads
Section titled “PDF and Binary Downloads”// Download PDF with specific headersdownloadPdf(id: string): Observable<Blob> { return this.http.get(`/api/documents/${id}/pdf`, { responseType: 'blob', headers: new HttpHeaders({ Accept: 'application/pdf' }), }).pipe( map(blob => { this.fileService.saveBlob(blob, `document-${id}.pdf`); return blob; }) );}Best Practices
Section titled “Best Practices”- Always use
responseType: 'blob'for binary file downloads - Use
URL.createObjectURL()to generate downloadable URLs in memory - Call
URL.revokeObjectURL()after download to prevent memory leaks - Show download progress for large files (>10MB)
- Handle download failures gracefully — retry with exponential backoff
- Check Content-Type header to ensure expected file type
- Use
finalizeoperator to reset loading states - Consider streaming for very large files (>100MB)
Common Mistakes
Section titled “Common Mistakes”- Using
responseType: 'json'for binary files (corrupts data) - Forgetting to revoke
ObjectURL(memory leak) - Not handling network interruptions during large downloads
- Blocking the UI during download — keep it responsive
- Not checking Content-Disposition header for server-suggested filenames
Interview Questions
Section titled “Interview Questions”- How do you download a file using HttpClient?
- What is
responseType: 'blob'and when do you use it? - How do you track download progress?
- How do you trigger a file download in the browser (save-as dialog)?
- What are the memory considerations for large file downloads?
Summary
Section titled “Summary”File download uses responseType: 'blob' and URL.createObjectURL() for saving files. Track progress with reportProgress, handle errors gracefully, and always clean up memory with revokeObjectURL().