Complete Solution for Downloading PDF Files from REST API in Angular 6

Nov 29, 2025 · Programming · 10 views · 7.8

Keywords: Angular 6 | File Download | REST API | Blob Handling | HttpClient

Abstract: This article provides a comprehensive analysis of common issues and solutions when downloading PDF files from REST APIs in Angular 6 applications. It covers key technical aspects including HttpClient response type configuration, Blob object handling, and browser compatibility, with complete code examples and best practices. The article also delves into server-side Spring Boot file return mechanisms to help developers fully understand file download implementation principles.

Problem Background and Error Analysis

When implementing file download functionality in Angular 6 applications, developers often encounter the <span style="font-family: monospace;">"Unexpected token % in JSON at position 0"</span> error. The root cause of this error is that HttpClient defaults to parsing responses as JSON format, while file downloads return binary data streams.

When the server returns a PDF file, the response body contains binary data, but Angular's HttpClient attempts to parse it as JSON, causing parsing failures. The <span style="font-family: monospace;">"%"</span> character in the error message is typically the beginning portion of the PDF file's binary data, confirming the improper response type configuration.

Server-Side Implementation Details

In the Spring Boot backend, the correct file download implementation should use <span style="font-family: monospace;">ResponseEntity<InputStreamResource></span>:

@GetMapping("/help/pdf2")
public ResponseEntity<InputStreamResource> getPdf2() {
    Resource resource = new ClassPathResource("/pdf-sample.pdf");
    long contentLength = 0;
    InputStream inputStream = null;
    
    try {
        inputStream = resource.getInputStream();
        contentLength = resource.contentLength();
    } catch (IOException e) {
        e.printStackTrace();
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }
    
    return ResponseEntity.ok()
            .contentLength(contentLength)
            .contentType(MediaType.parseMediaType("application/pdf"))
            .body(new InputStreamResource(inputStream));
}

This implementation ensures:

Angular Service Layer Optimization

In the Angular service, the key configuration is properly setting <span style="font-family: monospace;">responseType: 'blob'</span>:

getPdf(): Observable<Blob> {
    const httpOptions = {
        responseType: 'blob' as 'json'
    };
    
    return this.http.get(`${this.BASE_URL}/help/pdf2`, httpOptions);
}

Several important details to note here:

Component Layer Implementation and Browser Compatibility

When handling download responses in components, create Blob objects and trigger downloads:

downloadPdf(): void {
    this.downloadService.getPdf().subscribe((data: Blob) => {
        const blob = new Blob([data], { type: 'application/pdf' });
        
        // Create download link
        const downloadUrl = window.URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = downloadUrl;
        link.download = 'help.pdf';
        
        // Trigger download
        link.click();
        
        // Clean up resources
        window.URL.revokeObjectURL(downloadUrl);
    });
}

For better browser compatibility, consider this enhanced implementation:

downloadPdfEnhanced(fileName: string): void {
    this.downloadService.getPdf().subscribe((data: Blob) => {
        const blob = new Blob([data], { type: 'application/pdf' });
        
        // IE/Edge special handling
        if (window.navigator && (window.navigator as any).msSaveOrOpenBlob) {
            (window.navigator as any).msSaveOrOpenBlob(blob, fileName);
            return;
        }
        
        const downloadUrl = window.URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = downloadUrl;
        link.download = fileName;
        
        // Firefox compatibility handling
        link.dispatchEvent(new MouseEvent('click', {
            bubbles: true,
            cancelable: true,
            view: window
        }));
        
        // Delayed resource cleanup
        setTimeout(() => {
            window.URL.revokeObjectURL(downloadUrl);
            link.remove();
        }, 100);
    });
}

Error Handling and Best Practices

In practical applications, comprehensive error handling should be added:

downloadPdfWithErrorHandling(): void {
    this.downloadService.getPdf().subscribe({
        next: (data: Blob) => {
            try {
                const blob = new Blob([data], { type: 'application/pdf' });
                const downloadUrl = window.URL.createObjectURL(blob);
                const link = document.createElement('a');
                link.href = downloadUrl;
                link.download = 'help.pdf';
                link.click();
                window.URL.revokeObjectURL(downloadUrl);
            } catch (error) {
                console.error('Download processing error:', error);
                // Display user-friendly error messages
            }
        },
        error: (error) => {
            console.error('API request error:', error);
            // Handle network errors, authentication failures, etc.
        }
    });
}

Performance Optimization Considerations

For large file downloads, consider the following optimization strategies:

Security Considerations

When implementing file download functionality, pay attention to the following security aspects:

Through this complete implementation solution, developers can avoid common file download errors and provide stable and reliable file download functionality.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.