WinUI3应用中WebView2结合PDF.js无法渲染PDF问题排查
问题原因
- 应用沙箱权限限制:打包后的WinUI3应用运行在系统受限沙箱中,以
ms-appx-web://协议加载的WebView2页面,无法通过file:///协议直接访问本地文件系统中的文件——这是UWP/WinUI3的安全机制,防止应用未经授权访问用户文件。 - 路径格式错误:你构造的本地文件URL存在格式问题,Windows路径的盘符后需用
/分隔,但示例中生成的URL是C%3A%2MyTest(正确应为C:/MyTest),转义错误导致路径无法被PDF.js识别。
解决方案
推荐两种可靠的解决方法,根据你的场景选择:
方法一:通过WebView2虚拟主机映射访问文件
适合需要保留文件原存储位置的场景,将PDF文件所在目录映射为WebView2可访问的虚拟主机:
private async void PickAFileButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) { var openPicker = new Windows.Storage.Pickers.FileOpenPicker(); var window = App.MainWindow; var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(window); WinRT.Interop.InitializeWithWindow.Initialize(openPicker, hWnd); openPicker.ViewMode = PickerViewMode.Thumbnail; openPicker.FileTypeFilter.Add(".pdf"); var file = await openPicker.PickSingleFileAsync(); if (file != null) { await PdfWebView.EnsureCoreWebView2Async(); // 映射PDF.js资源目录到虚拟主机 var pdfjsPath = Path.Combine(AppContext.BaseDirectory, "Assets", "pdfjs"); PdfWebView.CoreWebView2.SetVirtualHostNameToFolderMapping( "appassets", pdfjsPath, CoreWebView2HostResourceAccessKind.Allow); // 映射PDF文件所在的父目录到虚拟主机 var pdfParentFolder = Path.GetDirectoryName(file.Path); PdfWebView.CoreWebView2.SetVirtualHostNameToFolderMapping( "pdfhost", pdfParentFolder, CoreWebView2HostResourceAccessKind.Allow); // 构造虚拟主机下的PDF文件URL var fileName = Path.GetFileName(file.Path); var pdfVirtualUrl = $"https://pdfhost/{Uri.EscapeDataString(fileName)}"; // 使用虚拟主机URL加载PDF.js viewer,并传递PDF地址 var pdfjsViewerUri = "https://appassets/web/viewer.html"; PdfWebView.Source = new Uri($"{pdfjsViewerUri}?file={Uri.EscapeDataString(pdfVirtualUrl)}"); } }
注意点
- 虚拟主机必须使用
https://协议(WebView2强制要求) - 同时通过虚拟主机访问PDF.js的
viewer.html,避免跨源访问被拦截
方法二:将PDF转为Base64数据URL加载
适合小体积PDF文件,直接读取文件内容转为Base64格式,通过data: URL传递给PDF.js:
private async void PickAFileButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) { var openPicker = new Windows.Storage.Pickers.FileOpenPicker(); var window = App.MainWindow; var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(window); WinRT.Interop.InitializeWithWindow.Initialize(openPicker, hWnd); openPicker.ViewMode = PickerViewMode.Thumbnail; openPicker.FileTypeFilter.Add(".pdf"); var file = await openPicker.PickSingleFileAsync(); if (file != null) { await PdfWebView.EnsureCoreWebView2Async(); var pdfjsPath = Path.Combine(AppContext.BaseDirectory, "Assets", "pdfjs"); PdfWebView.CoreWebView2.SetVirtualHostNameToFolderMapping( "appassets", pdfjsPath, CoreWebView2HostResourceAccessKind.Allow); // 读取PDF文件并转为Base64数据URL var fileBytes = await File.ReadAllBytesAsync(file.Path); var base64Pdf = Convert.ToBase64String(fileBytes); var dataUrl = $"data:application/pdf;base64,{base64Pdf}"; var pdfjsViewerUri = "https://appassets/web/viewer.html"; PdfWebView.Source = new Uri($"{pdfjsViewerUri}?file={Uri.EscapeDataString(dataUrl)}"); } }
额外注意事项
- 确保PDF.js的
viewer.html允许加载自定义来源的文件:默认PDF.js可能会限制文件来源,可修改viewer.js中的ALLOWED_FILE_ORIGINS配置,添加虚拟主机域名或允许所有来源(仅测试环境使用) - 打包应用时,将
Assets/pdfjs目录下的所有文件设置为“复制到输出目录”(在Visual Studio中右键文件→属性→复制到输出目录→选择“始终复制”)
内容的提问来源于stack exchange,提问作者Ogaili
相关产品推荐
相关产品推荐

