You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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();
}

注意事项

  1. 确保pdf.js和pdf.worker.js已添加到项目中:
    • Android:放在Resources/raw或Assets目录,设置生成操作为AndroidAsset
    • iOS:放在Resources目录,设置生成操作为BundleResource
    • Windows:放在wwwroot目录,设置生成操作为Content
  2. 临时文件使用后可选择删除,避免占用存储空间
  3. Android平台需注意文件访问权限,临时目录一般无需额外权限

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 21:39:52