我正在通过IIS Web应用程序合并XPS文件,但在合并过程中,它会使文档引用文件句柄处于打开状态.在Windows WPF或控制台应用程序中,我不担心句柄,因为它们会在应用程序关闭后释放.回收应用程序池也会有效地关闭句柄.但是,由于缓存和性能,我无法仅仅为了关闭文件句柄而回收应用程序池.
合并完成后有没有办法关闭这些文件句柄?我尝试了一些不同的合并方法,但以下代码给了我最好的结果:
private DocumentPaginator CreateMergedDocument(IEnumerable<string> xpsFiles) {
using ( MemoryStream mergedStream = new MemoryStream() ) {
using ( var pkg = Package.Open( mergedStream, FileMode.Create, FileAccess.ReadWrite ) ) {
var pack = "pack://merged.xps";
var uri = new Uri( pack, UriKind.Absolute );
PackageStore.AddPackage( uri, pkg );
using ( XpsDocument mergedDocument =
new XpsDocument( pkg, CompressionOption.Maximum, pack ) ) {
FixedDocumentSequence seqNew = new FixedDocumentSequence();
foreach ( string sourceDocument in xpsFiles ) {
using ( XpsDocument xpsOld =
new XpsDocument( sourceDocument, FileAccess.Read ) ) {
FixedDocumentSequence seqOld = xpsOld.GetFixedDocumentSequence();
foreach ( DocumentReference dr in seqOld.References ) {
DocumentReference newDocumentReference = new DocumentReference();
newDocumentReference.Source = dr.Source;
( newDocumentReference as IUriContext ).BaseUri =
( dr as IUriContext ).BaseUri;
seqNew.References.Add( newDocumentReference );
}
}
}
XpsDocumentWriter xpsWriter = XpsDocument.CreateXpsDocumentWriter( mergedDocument );
xpsWriter.Write( seqNew );
PackageStore.RemovePackage( uri );
return seqNew.DocumentPaginator;
}
}
}
}
我找到了打开xpsWriter.Write(seqNew)代码行的文件句柄.这是预期的,因为它需要加载文件以复制到新的FixedDocumentSequence中.但是,如果XpsDocumentWriter是一次性的并清理其非托管资源/文件句柄,那将是很好的.
在我的研究期间,我尝试了其他一些方法.我尝试过的一种方法是将每个页面可视化写入SerializerWriterCollator.我研究的另一种方法是处理FixedPage内容并将URI更新为复制的图像和字体流,并将原始XAML写回XmlWriter.两者都没有奏效.使用页面视觉效果和SerializerWriterCollator,我在合并的XPS上获得了不正确的页面大小,并且它正在切断内容.使用原始XmlWriter方法,它在合并的XPS中混淆了一些图像并错误地显示了一些页面内容.
解决方法:
经过一些研究,我发现用于在STA线程中运行我的函数的原始代码没有正确释放文件句柄和非托管资源.我们有一个自定义线程类,它具有调用操作和在线程上运行连接的自定义实现.操作方法完成后,线程未正确退出.为了解决这个问题,我将代码包装在类似于以下的块中:
private static readonly TaskScheduler _staScheduler = new StaTaskScheduler( 1 );
[Route( "merge" )]
[HttpGet]
public async Task<HttpResponseMessage> MergeXps() {
var paginator = await Task<DocumentPaginator>.Factory.StartNew(
() =>
{
var xpsFiles = Directory.GetFiles( "C:\\Xps", "*.xps" );
var documentPaginator = CreateMergedDocument( xpsFiles );
return documentPaginator;
},
CancellationToken.None,
TaskCreationOptions.None,
_staScheduler );
var response = new HttpResponseMessage();
response.Content = new StringContent( $"Merged Document Page Count: {paginator.PageCount}" );
response.StatusCode = HttpStatusCode.OK;
return response;
}