如何在Angular中展示本地PDF文件并实现打印功能?
Hey there! I totally get the frustration here—trying to load local PDFs directly in Angular runs straight into browser security restrictions, which is why your iframe approach is throwing errors. Let’s break down the best ways to solve this:
Why the iframe approach fails
Browsers block web apps (running on http://localhost or a server) from accessing local file:// URLs due to same-origin policy and security safeguards. Even if you use a third-party viewer like Google Drive, it can’t access files on your local filesystem either.
Recommended Solution: Let Users Upload the PDF (Safe & Universal)
The most secure and reliable way is to have users upload the PDF file directly to your Angular app. You can then use PDF.js (a popular open-source PDF rendering library) to display it in the browser, and add custom print functionality. Here’s a step-by-step implementation:
Step 1: Install dependencies
First, add PDF.js and its TypeScript types to your project:
npm install pdfjs-dist npm install @types/pdfjs-dist --save-dev
Step 2: Component Template (app.component.html)
Add a file input, a container for PDF pages, and a print button:
<input type="file" accept=".pdf" (change)="onFileSelected($event)" class="pdf-upload"> <div #pdfContainer class="pdf-container"></div> <button (click)="printPDF()" [disabled]="!pdfLoaded" class="print-btn">Print PDF</button>
Step 3: Component Logic (app.component.ts)
Implement file reading, PDF rendering, and print functionality:
import { Component, ViewChild, ElementRef } from '@angular/core'; import * as pdfjsLib from 'pdfjs-dist'; import { PDFDocumentProxy } from 'pdfjs-dist'; // Set PDF.js worker source (use a CDN or local file) pdfjsLib.GlobalWorkerOptions.workerSrc = '//cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { @ViewChild('pdfContainer') pdfContainer!: ElementRef; pdfLoaded = false; renderedPages: HTMLCanvasElement[] = []; onFileSelected(event: Event): void { const file = (event.target as HTMLInputElement).files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = async (e) => { const arrayBuffer = e.target?.result as ArrayBuffer; const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise; this.renderedPages = []; this.pdfContainer.nativeElement.innerHTML = ''; // Render each PDF page to a canvas element for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) { const page = await pdf.getPage(pageNum); const viewport = page.getViewport({ scale: 1.2 }); // Adjust scale for readability const canvas = document.createElement('canvas'); const context = canvas.getContext('2d')!; canvas.height = viewport.height; canvas.width = viewport.width; await page.render({ canvasContext: context, viewport: viewport }).promise; this.pdfContainer.nativeElement.appendChild(canvas); this.renderedPages.push(canvas); } this.pdfLoaded = true; }; reader.readAsArrayBuffer(file); } printPDF(): void { const printWindow = window.open('', '_blank'); if (!printWindow) return; printWindow.document.write(` <html> <head> <title>Print PDF</title> <style> body { margin: 0; padding: 1rem; } canvas { display: block; margin: 0 auto; max-width: 100%; } </style> </head> <body> `); // Add all rendered pages to the print window this.renderedPages.forEach(page => { printWindow.document.write(page.outerHTML); }); printWindow.document.write('</body></html>'); printWindow.document.close(); // Trigger print after the content loads setTimeout(() => printWindow.print(), 300); } }
Step 4: Basic Styling (app.component.css)
.pdf-upload { margin: 1rem 0; padding: 0.5rem; } .pdf-container { margin: 1rem 0; min-height: 400px; } .print-btn { padding: 0.5rem 1.5rem; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; } .print-btn:disabled { background-color: #6c757d; cursor: not-allowed; }
Alternative: Browser Security Workaround (Testing Only)
If you’re building an internal tool in a fully trusted environment and need to load local PDFs without user upload, you can disable browser security restrictions. This is NOT recommended for production as it exposes users to security risks:
For Chrome/Edge:
Close all browser instances, then launch it with these flags:
chrome.exe --allow-file-access-from-files --disable-web-security
Then you can use an iframe with the local file path:
<iframe src="file:///C:/Users/xx/Desktop/xyz.pdf" frameBorder="0" scrolling="auto" height="100%" width="100%"></iframe>
Again, this is strictly for testing—never use this for public-facing apps.
Final Notes
The user upload approach is the only way to comply with modern browser security standards and work reliably across all environments. It gives you full control over rendering and printing, and avoids any cross-origin or file access errors.
内容的提问来源于stack exchange,提问作者ozer

