如何在MAUI中展示从数据库获取的PDF字节数组?
在MAUI中展示API获取的PDF字节数组
已实现的本地PDF展示方案
你已经成功通过pdf.js加载本地嵌入的PDF文件,两种可行实现如下:
方式一:直接在页面构造中设置WebView源
MainPage.xaml.cs
public partial class MainPage : ContentPage { private readonly MainViewModel viewModel; public MainPage(MainViewModel viewModel) { InitializeComponent(); BindingContext = viewModel; this.viewModel = viewModel; #if ANDROID Microsoft.Maui.Handlers.WebViewHandler.Mapper.AppendToMapping("pdfviewer", (handler, view) => { handler.PlatformView.Settings.AllowFileAccess = true; handler.PlatformView.Settings.AllowFileAccessFromFileURLs = true; handler.PlatformView.Settings.AllowUniversalAccessFromFileURLs = true; }); pdfview.Source = $"file:///android_asset/pdfjs/web/viewer.html?file=file:///android_asset/{WebUtility.UrlEncode("mypdf.pdf")}"; #else pdfview.Source = "mypdf.pdf"; #endif } }
方式二:通过ViewModel绑定WebView源
XAML代码
<WebView x:Name="pdfview" HorizontalOptions="FillAndExpand" Source="{Binding Source}" VerticalOptions="FillAndExpand" />
ViewModel代码
[ObservableProperty] private string source; [RelayCommand] public async Task<bool> ShowPDFBundledNewAsync() { #if ANDROID Microsoft.Maui.Handlers.WebViewHandler.Mapper.AppendToMapping("pdfviewer", (handler, view) => { handler.PlatformView.Settings.AllowFileAccess = true; handler.PlatformView.Settings.AllowFileAccessFromFileURLs = true; handler.PlatformView.Settings.AllowUniversalAccessFromFileURLs = true; }); #endif Source = $"file:///android_asset/pdfjs/web/viewer.html?file=file:///android_asset/{WebUtility.UrlEncode("mypdf.pdf")}"; return true; }
MainPage.xaml.cs
public partial class MainPage : ContentPage { private readonly MainViewModel viewModel; public MainPage(MainViewModel viewModel) { InitializeComponent(); BindingContext = viewModel; this.viewModel = viewModel; } protected override void OnAppearing() { base.OnAppearing(); _ = viewModel.ShowPDFBundledNewAsync().Result; } }
当前问题
需要将上述方案中的本地PDF文件替换为API返回的PDF字节数组,尝试将字节数组转为Base64嵌入HTML后页面无内容,需要找到可行的实现方式,且要求页面加载时直接显示PDF。
解决方案
方法一:将字节数组保存为临时文件加载
这种方式复用你已有的pdf.js viewer逻辑,只需将字节数组写入临时目录,再指向该文件路径即可:
ViewModel代码
[ObservableProperty] private string source; // 假设你从API获取到的字节数组为pdfBytes private byte[] pdfBytes; [RelayCommand] public async Task LoadPdfFromBytesAsync() { #if ANDROID Microsoft.Maui.Handlers.WebViewHandler.Mapper.AppendToMapping("pdfviewer", (handler, view) => { handler.PlatformView.Settings.AllowFileAccess = true; handler.PlatformView.Settings.AllowFileAccessFromFileURLs = true; handler.PlatformView.Settings.AllowUniversalAccessFromFileURLs = true; }); #endif // 创建临时文件 var tempPath = Path.Combine(FileSystem.CacheDirectory, "temp.pdf"); await File.WriteAllBytesAsync(tempPath, pdfBytes); // 根据平台构造文件路径 string fileUrl; #if ANDROID fileUrl = $"file://{tempPath}"; #elif IOS fileUrl = tempPath; #elif WINDOWS fileUrl = $"file:///{tempPath.Replace("\\", "/")}"; #endif // 设置WebView源为pdf.js viewer,指向临时文件 Source = $"file:///android_asset/pdfjs/web/viewer.html?file={WebUtility.UrlEncode(fileUrl)}"; }
在页面OnAppearing中调用该命令即可加载:
protected override async void OnAppearing() { base.OnAppearing(); // 先调用API获取pdfBytes,再执行加载命令 pdfBytes = await YourApiService.GetPdfBytesAsync(); await viewModel.LoadPdfFromBytesAsync(); }
方法二:优化Base64嵌入HTML的方式
之前的实现仅渲染第一页,且可能存在pdf.js路径错误问题,以下是修正后的多页渲染方案:
ViewModel代码
[ObservableProperty] private HtmlWebViewSource htmlWebViewSource; private byte[] pdfBytes; [RelayCommand] public async Task LoadPdfFromBase64Async() { var base64Pdf = Convert.ToBase64String(pdfBytes); // 确保pdf.js和pdf.worker.js在项目的对应目录中,BaseUrl设置正确 string baseUrl; #if ANDROID baseUrl = "file:///android_asset/"; #elif IOS baseUrl = NSBundle.MainBundle.BundlePath; #elif WINDOWS baseUrl = "ms-appx-web:///"; #endif var html = $@" <!DOCTYPE html> <html> <head> <meta charset=""utf-8""> <title>PDF Viewer</title> <script src=""pdf.js""></script> <style> .pdf-page {{ margin: 10px auto; border: 1px solid #ccc; }} </style> </head> <body> <div id=""pdf-container""></div> <script> async function renderPdf() {{ const pdfData = atob('{base64Pdf}'); const pdfjsLib = window['pdfjs-dist/build/pdf']; pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdf.worker.js'; const pdf = await pdfjsLib.getDocument({{data: pdfData}}).promise; const container = document.getElementById('pdf-container'); // 渲染所有页面 for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {{ const page = await pdf.getPage(pageNum); const viewport = page.getViewport({{scale: 1.5}}); const canvas = document.createElement('canvas'); canvas.className = 'pdf-page'; canvas.height = viewport.height; canvas.width = viewport.width; const context = canvas.getContext('2d'); await page.render({{ canvasContext: context, viewport: viewport }}).promise; container.appendChild(canvas); }} }} renderPdf(); </script> </body> </html>"; HtmlWebViewSource = new HtmlWebViewSource { BaseUrl = baseUrl, Html = html }; }
XAML代码
<WebView HorizontalOptions="FillAndExpand" Source="{Binding HtmlWebViewSource}" VerticalOptions="FillAndExpand" />
页面加载时调用命令:
protected override async void OnAppearing() { base.OnAppearing(); pdfBytes = await YourApiService.GetPdfBytesAsync(); await viewModel.LoadPdfFromBase64Async(); }
注意事项
- 确保
pdf.js和pdf.worker.js已添加到项目中:- Android:放在
Resources/raw或Assets目录,设置生成操作为AndroidAsset - iOS:放在
Resources目录,设置生成操作为BundleResource - Windows:放在
wwwroot目录,设置生成操作为Content
- Android:放在
- 临时文件使用后可选择删除,避免占用存储空间
- Android平台需注意文件访问权限,临时目录一般无需额外权限
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

