Skip to content

Commit ad68d1b

Browse files
authored
feat(PdfReader): add SetPdfStreamAsync/SetPdfBase64DataAsync instance method (#817)
* feat: 增加 SetPdfStreamAsync 方法 * chore: bump version 10.0.13
1 parent 268a13b commit ad68d1b

3 files changed

Lines changed: 147 additions & 20 deletions

File tree

src/components/BootstrapBlazor.PdfReader/BootstrapBlazor.PdfReader.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Razor">
22

33
<PropertyGroup>
4-
<Version>10.0.12</Version>
4+
<Version>10.0.13</Version>
55
</PropertyGroup>
66

77
<PropertyGroup>

src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs

Lines changed: 122 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public partial class PdfReader
132132
/// </summary>
133133
/// <remarks>优先使用 <see cref="Url"/> 未提供 <see cref="Url"/> 时会尝试调用此回调获得流进行渲染</remarks>
134134
[Parameter]
135-
public Func<Task<Stream>>? OnGetStreamAsync { get; set; }
135+
public Func<Task<Stream?>>? OnGetStreamAsync { get; set; }
136136

137137
[Inject, NotNull]
138138
private IStringLocalizer<PdfReader>? Localizer { get; set; }
@@ -156,6 +156,8 @@ public partial class PdfReader
156156
private bool _enableThumbnails = true;
157157
private bool _showToolbar = true;
158158
private PdfReaderFitMode _fitMode;
159+
private string _lastStreamHash = string.Empty;
160+
private long _lastStreamLength = 0;
159161

160162
/// <summary>
161163
/// <inheritdoc/>
@@ -188,14 +190,16 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
188190
{
189191
_url = Url;
190192
_currentPage = CurrentPage;
191-
_enableThumbnails = EnableThumbnails;
193+
_currentRotation = CurrentRotation;
192194
_showToolbar = ShowToolbar;
195+
_enableThumbnails = EnableThumbnails;
193196
_fitMode = FitMode;
194197
}
195198

196199
if (_url != Url)
197200
{
198201
_url = Url;
202+
_lastStreamHash = string.Empty;
199203
await InvokeVoidAsync("setUrl", Id, _url);
200204
}
201205
if (_currentPage != CurrentPage)
@@ -229,8 +233,124 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
229233
_fitMode = FitMode;
230234
await SetFitMode(_fitMode);
231235
}
236+
if (string.IsNullOrEmpty(Url))
237+
{
238+
Stream? stream = null;
239+
if (OnGetStreamAsync != null)
240+
{
241+
stream = await OnGetStreamAsync();
242+
}
243+
244+
await SetPdfStream(stream);
245+
}
246+
}
247+
248+
private async Task SetPdfStream(Stream? stream)
249+
{
250+
if (stream == null || stream == Stream.Null)
251+
{
252+
_lastStreamHash = string.Empty;
253+
_lastStreamLength = 0;
254+
await InvokeVoidAsync("setData", Id, null);
255+
return;
256+
}
257+
258+
byte[] pdfBytes = await GetBytes(stream);
259+
260+
var currentLength = pdfBytes.Length;
261+
if (_lastStreamLength != currentLength)
262+
{
263+
_lastStreamLength = currentLength;
264+
await InvokeVoidAsync("setData", Id, pdfBytes);
265+
return;
266+
}
267+
268+
#if NET6_0
269+
var currentHash = ComputerHash(pdfBytes);
270+
#else
271+
var currentHash = await ComputerHash(stream);
272+
#endif
273+
if (_lastStreamHash != currentHash)
274+
{
275+
_lastStreamHash = currentHash;
276+
await InvokeVoidAsync("setData", Id, pdfBytes);
277+
}
278+
}
279+
280+
private async Task<byte[]?> GetPdfStreamDataAsync()
281+
{
282+
byte[]? pdfBytes = null;
283+
if (OnGetStreamAsync != null)
284+
{
285+
var stream = await OnGetStreamAsync();
286+
if (stream == null || stream == Stream.Null)
287+
{
288+
_lastStreamHash = string.Empty;
289+
_lastStreamLength = 0;
290+
}
291+
else
292+
{
293+
pdfBytes = await GetBytes(stream);
294+
}
295+
}
296+
return pdfBytes;
297+
}
298+
299+
private static async Task<byte[]> GetBytes(Stream stream)
300+
{
301+
using var memoryStream = new MemoryStream();
302+
await stream.CopyToAsync(memoryStream);
303+
return memoryStream.ToArray();
232304
}
233305

306+
/// <summary>
307+
/// 设置 PDF 流数据方法
308+
/// </summary>
309+
/// <param name="stream"></param>
310+
/// <returns></returns>
311+
public async Task SetPdfStreamAsync(Stream stream)
312+
{
313+
using var memoryStream = new MemoryStream();
314+
await stream.CopyToAsync(memoryStream);
315+
var pdfBytes = memoryStream.ToArray();
316+
_lastStreamLength = pdfBytes.Length;
317+
#if NET6_0
318+
_lastStreamHash = ComputerHash(pdfBytes);
319+
#else
320+
_lastStreamHash = await ComputerHash(stream);
321+
#endif
322+
await InvokeVoidAsync("setData", Id, pdfBytes);
323+
}
324+
325+
/// <summary>
326+
/// 设置 Pdf Base64 数据方法
327+
/// </summary>
328+
/// <param name="base64Data"></param>
329+
/// <returns></returns>
330+
public async Task SetPdfBase64DataAsync(string base64Data)
331+
{
332+
var pdfBytes = Convert.FromBase64String(base64Data);
333+
await InvokeVoidAsync("setData", Id, pdfBytes);
334+
}
335+
336+
#if NET6_0
337+
private static string ComputerHash(byte[] data)
338+
{
339+
var hashBytes = System.Security.Cryptography.SHA256.HashData(data);
340+
return Convert.ToBase64String(hashBytes);
341+
}
342+
#else
343+
private static async Task<string> ComputerHash(Stream stream)
344+
{
345+
if (stream.CanSeek)
346+
{
347+
stream.Seek(0, SeekOrigin.Begin);
348+
}
349+
var hashBytes = await System.Security.Cryptography.SHA256.HashDataAsync(stream);
350+
return Convert.ToBase64String(hashBytes);
351+
}
352+
#endif
353+
234354
/// <summary>
235355
/// <inheritdoc/>
236356
/// </summary>
@@ -278,19 +398,6 @@ protected override async Task InvokeInitAsync()
278398
/// <returns></returns>
279399
public Task RotateRight() => InvokeVoidAsync("rotate", Id, 90);
280400

281-
private async Task<byte[]?> GetPdfStreamDataAsync()
282-
{
283-
byte[]? pdfBytes = null;
284-
if (OnGetStreamAsync != null)
285-
{
286-
using var memoryStream = new MemoryStream();
287-
var stream = await OnGetStreamAsync();
288-
await stream.CopyToAsync(memoryStream);
289-
pdfBytes = memoryStream.ToArray();
290-
}
291-
return pdfBytes;
292-
}
293-
294401
/// <summary>
295402
/// 页面开始初始化时回调方法
296403
/// </summary>

src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ export async function setUrl(id, url) {
3636

3737
const { options } = pdf;
3838
options.url = url;
39-
options.data = null;
4039
await loadPdf(pdf);
4140
}
4241

@@ -48,12 +47,33 @@ export async function setData(id, data) {
4847
return;
4948
}
5049

51-
const { options } = pdf;
52-
options.url = null;
53-
options.data = data;
50+
const { options, objectUrl } = pdf;
51+
if (objectUrl) {
52+
URL.revokeObjectURL(objectUrl);
53+
}
54+
55+
options.url = createObjectURLFromByte(data);
56+
options.data = null;
57+
pdf.objectUrl = options.url;
5458
await loadPdf(pdf);
5559
}
5660

61+
const createObjectURLFromBase64 = base64Data => {
62+
const binaryString = atob(base64Data);
63+
const bytes = new Uint8Array(binaryString.length);
64+
for (let i = 0; i < binaryString.length; i++) {
65+
bytes[i] = binaryString.charCodeAt(i);
66+
}
67+
68+
const blob = new Blob([bytes], { type: 'application/pdf' });
69+
return URL.createObjectURL(blob);
70+
}
71+
72+
const createObjectURLFromByte = bytes => {
73+
const blob = new Blob([bytes], { type: 'application/pdf' });
74+
return URL.createObjectURL(blob);
75+
}
76+
5777
export function setScaleValue(id, value) {
5878
const { pdfViewer } = Data.get(id);
5979
if (pdfViewer) {

0 commit comments

Comments
 (0)