diff --git a/src/components/BootstrapBlazor.PdfReader/BootstrapBlazor.PdfReader.csproj b/src/components/BootstrapBlazor.PdfReader/BootstrapBlazor.PdfReader.csproj index 46472075..c56c856c 100644 --- a/src/components/BootstrapBlazor.PdfReader/BootstrapBlazor.PdfReader.csproj +++ b/src/components/BootstrapBlazor.PdfReader/BootstrapBlazor.PdfReader.csproj @@ -1,7 +1,7 @@  - 10.0.12 + 10.0.13 diff --git a/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs b/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs index 077596a7..82520689 100644 --- a/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs +++ b/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs @@ -132,7 +132,7 @@ public partial class PdfReader /// /// 优先使用 未提供 时会尝试调用此回调获得流进行渲染 [Parameter] - public Func>? OnGetStreamAsync { get; set; } + public Func>? OnGetStreamAsync { get; set; } [Inject, NotNull] private IStringLocalizer? Localizer { get; set; } @@ -156,6 +156,8 @@ public partial class PdfReader private bool _enableThumbnails = true; private bool _showToolbar = true; private PdfReaderFitMode _fitMode; + private string _lastStreamHash = string.Empty; + private long _lastStreamLength = 0; /// /// @@ -188,14 +190,16 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { _url = Url; _currentPage = CurrentPage; - _enableThumbnails = EnableThumbnails; + _currentRotation = CurrentRotation; _showToolbar = ShowToolbar; + _enableThumbnails = EnableThumbnails; _fitMode = FitMode; } if (_url != Url) { _url = Url; + _lastStreamHash = string.Empty; await InvokeVoidAsync("setUrl", Id, _url); } if (_currentPage != CurrentPage) @@ -229,8 +233,124 @@ protected override async Task OnAfterRenderAsync(bool firstRender) _fitMode = FitMode; await SetFitMode(_fitMode); } + if (string.IsNullOrEmpty(Url)) + { + Stream? stream = null; + if (OnGetStreamAsync != null) + { + stream = await OnGetStreamAsync(); + } + + await SetPdfStream(stream); + } + } + + private async Task SetPdfStream(Stream? stream) + { + if (stream == null || stream == Stream.Null) + { + _lastStreamHash = string.Empty; + _lastStreamLength = 0; + await InvokeVoidAsync("setData", Id, null); + return; + } + + byte[] pdfBytes = await GetBytes(stream); + + var currentLength = pdfBytes.Length; + if (_lastStreamLength != currentLength) + { + _lastStreamLength = currentLength; + await InvokeVoidAsync("setData", Id, pdfBytes); + return; + } + +#if NET6_0 + var currentHash = ComputerHash(pdfBytes); +#else + var currentHash = await ComputerHash(stream); +#endif + if (_lastStreamHash != currentHash) + { + _lastStreamHash = currentHash; + await InvokeVoidAsync("setData", Id, pdfBytes); + } + } + + private async Task GetPdfStreamDataAsync() + { + byte[]? pdfBytes = null; + if (OnGetStreamAsync != null) + { + var stream = await OnGetStreamAsync(); + if (stream == null || stream == Stream.Null) + { + _lastStreamHash = string.Empty; + _lastStreamLength = 0; + } + else + { + pdfBytes = await GetBytes(stream); + } + } + return pdfBytes; + } + + private static async Task GetBytes(Stream stream) + { + using var memoryStream = new MemoryStream(); + await stream.CopyToAsync(memoryStream); + return memoryStream.ToArray(); } + /// + /// 设置 PDF 流数据方法 + /// + /// + /// + public async Task SetPdfStreamAsync(Stream stream) + { + using var memoryStream = new MemoryStream(); + await stream.CopyToAsync(memoryStream); + var pdfBytes = memoryStream.ToArray(); + _lastStreamLength = pdfBytes.Length; +#if NET6_0 + _lastStreamHash = ComputerHash(pdfBytes); +#else + _lastStreamHash = await ComputerHash(stream); +#endif + await InvokeVoidAsync("setData", Id, pdfBytes); + } + + /// + /// 设置 Pdf Base64 数据方法 + /// + /// + /// + public async Task SetPdfBase64DataAsync(string base64Data) + { + var pdfBytes = Convert.FromBase64String(base64Data); + await InvokeVoidAsync("setData", Id, pdfBytes); + } + +#if NET6_0 + private static string ComputerHash(byte[] data) + { + var hashBytes = System.Security.Cryptography.SHA256.HashData(data); + return Convert.ToBase64String(hashBytes); + } +#else + private static async Task ComputerHash(Stream stream) + { + if (stream.CanSeek) + { + stream.Seek(0, SeekOrigin.Begin); + } + var hashBytes = await System.Security.Cryptography.SHA256.HashDataAsync(stream); + return Convert.ToBase64String(hashBytes); + } +#endif + /// /// /// @@ -278,19 +398,6 @@ protected override async Task InvokeInitAsync() /// public Task RotateRight() => InvokeVoidAsync("rotate", Id, 90); - private async Task GetPdfStreamDataAsync() - { - byte[]? pdfBytes = null; - if (OnGetStreamAsync != null) - { - using var memoryStream = new MemoryStream(); - var stream = await OnGetStreamAsync(); - await stream.CopyToAsync(memoryStream); - pdfBytes = memoryStream.ToArray(); - } - return pdfBytes; - } - /// /// 页面开始初始化时回调方法 /// diff --git a/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js b/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js index acc6fcab..cd52db19 100644 --- a/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js +++ b/src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js @@ -36,7 +36,6 @@ export async function setUrl(id, url) { const { options } = pdf; options.url = url; - options.data = null; await loadPdf(pdf); } @@ -48,12 +47,33 @@ export async function setData(id, data) { return; } - const { options } = pdf; - options.url = null; - options.data = data; + const { options, objectUrl } = pdf; + if (objectUrl) { + URL.revokeObjectURL(objectUrl); + } + + options.url = createObjectURLFromByte(data); + options.data = null; + pdf.objectUrl = options.url; await loadPdf(pdf); } +const createObjectURLFromBase64 = base64Data => { + const binaryString = atob(base64Data); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + + const blob = new Blob([bytes], { type: 'application/pdf' }); + return URL.createObjectURL(blob); +} + +const createObjectURLFromByte = bytes => { + const blob = new Blob([bytes], { type: 'application/pdf' }); + return URL.createObjectURL(blob); +} + export function setScaleValue(id, value) { const { pdfViewer } = Data.get(id); if (pdfViewer) {