我有一个工作解决方案,可以从Windows应用商店应用程序中的字节数组加载和呈现PDF文档。不过,最近一些用户报告了内存不足的错误。正如你在下面的代码中看到的,有一个流我没有处理掉。我已经把这句话评论掉了。如果我真的处理了那个流,那么PDF文档就不会再呈现了。它只是显示了一个完全白色的图像。有人能解释为什么以及如何加载和呈现PDF文档并处理所有一次性用品吗?
private static async Task<PdfDocument> LoadDocumentAsync(byte[] bytes)
{
using (var stream = new InMemoryRandomAccessStream())
{
await stream.WriteAsync(bytes.AsBuffer());
stream.Seek(0);
var fileStream = RandomAccessStreamReference.CreateFromStream(stream);
var inputStream = await fileStream.OpenReadAsync();
try
{
return await PdfDocument.LoadFromStreamAsync(inputStream);
}
finally
{
// do not dispose otherwise pdf does not load / render correctly. Not disposing though may cause memory issues.
// inputStream.Dispose();
}
}
}
以及呈现PDF 的代码
private static async Task<ObservableCollection<BitmapImage>> RenderPagesAsync(
PdfDocument document,
PdfPageRenderOptions options)
{
var items = new ObservableCollection<BitmapImage>();
if (document != null && document.PageCount > 0)
{
for (var pageIndex = 0; pageIndex < document.PageCount; pageIndex++)
{
using (var page = document.GetPage((uint)pageIndex))
{
using (var imageStream = new InMemoryRandomAccessStream())
{
await page.RenderToStreamAsync(imageStream, options);
await imageStream.FlushAsync();
var renderStream = RandomAccessStreamReference.CreateFromStream(imageStream);
using (var stream = await renderStream.OpenReadAsync())
{
var bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(stream);
items.Add(bitmapImage);
}
}
}
}
}
return items;
}
正如你所看到的,我在两个方法中都使用了RandomAccessStreamReference.CreateFromStream方法。我见过其他例子跳过这一步,直接使用InMemoryRandomAccessStream加载PDF文档或位图图像,但当时我还没能正确渲染PDF。图像将再次完全变白。正如我上面提到的,这段代码确实正确地呈现了PDF,但并没有处理掉所有可丢弃的东西。
为什么
我假设LoadFromStreamAsync(IRandomAccessStream)
不会将整个流解析为PdfDocument
对象,而是只解析主要的PDF字典并保存对IRandomAccessStream
的引用。
这实际上是明智的做法,如果用户最终只想呈现一个页面,甚至只想查询页面数量,为什么要将整个PDF解析成自己的对象(从资源角度来看,这可能是一项非常昂贵的操作)
稍后,当调用返回的PdfDocument
的其他方法(例如GetPage
)时,这些方法尝试从流中读取它们执行任务(例如渲染)所需的附加数据。不幸的是,在您的情况下,这意味着在finally { inputStream.Dispose(); }
之后
其他怎么办
您必须推迟inputStream.Dispose()
,直到PdfDocument
上的所有操作完成。这意味着您的代码可能会有一些细微的体系结构更改。可能将LoadDocumentAsync
代码作为帧移动到RenderPagesAsync
方法或其调用者中就足够了。