Xamarin.Forms中HTML转PDF并存储遇共享冲突问题求助
问题背景
在Xamarin.Forms中实现HTML转PDF并存储功能,先后尝试PDFSharpcore、Syncfusion.Xamarin.PDF、Xamarin.PDF等第三方库均未成功;采用Android原生WebView渲染HTML转PDF方案后,转换功能正常,但存储环节抛出**Sharing violation(共享冲突)**异常,异常路径为/storage/emulated/0/Android/data/com.bt.WasteTracker.intune/files/temp/Example_1677226869970.pdf,触发点在代码:
using (var stream = new FileStream(this.FilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
已确认目标文件夹写入权限,且排查过文件未被其他应用/进程占用,但问题仍存在。
核心原因分析
从提供的Android原生代码来看,PdfLayoutResultCallback中通过ParcelFileDescriptor.Open打开了目标文件的读写句柄,但未确保该句柄在操作完成后及时释放;后续FileStream尝试以FileShare.None模式打开同一文件时,就会因文件句柄未释放导致共享冲突。
修复思路及步骤
1. 确保ParcelFileDescriptor及时释放
在OnLayoutFinished方法中,使用using语句包裹ParcelFileDescriptor,保证文件句柄在Adapter.OnWrite操作完成后自动释放:
public override void OnLayoutFinished(PrintDocumentInfo info, bool changed) { try { var file = new Java.IO.File(PDFToHtml.FilePath); // 使用using自动释放文件句柄 using (var fileDescriptor = ParcelFileDescriptor.Open(file, ParcelFileMode.ReadWrite)) { var writeResultCallback = new PdfWriteResultCallback(PDFToHtml); Adapter.OnWrite(new PageRange[] { PageRange.AllPages }, fileDescriptor, new CancellationSignal(), writeResultCallback); } } catch { PDFToHtml.Status = PDFEnum.Failed; } base.OnLayoutFinished(info, changed); }
2. 调整FileStream的共享模式
如果必须在写入操作后立即读取/操作文件,可将FileShare参数调整为FileShare.ReadWrite,允许其他进程同时读写(需确保此时文件写入已完成):
using (var stream = new FileStream(this.FilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
3. 等待写入完成信号再操作文件
PdfWriteResultCallback的OnWriteFinished是PDF写入完成的回调,确保只有在收到该回调后,再执行FileStream相关的存储/读取操作,避免在文件写入过程中尝试打开文件:
- 在
PDFToHtml类中添加TaskCompletionSource来等待写入完成信号 - 在
OnWriteFinished中触发信号,再执行后续的文件操作
示例调整:
// 在PDFToHtml类中定义 public TaskCompletionSource<bool> WriteCompletionSource { get; } = new TaskCompletionSource<bool>(); // 在PdfWriteResultCallback的OnWriteFinished中 public override void OnWriteFinished(PageRange[] pages) { base.OnWriteFinished(pages); pDFToHtml.Status = PDFEnum.Completed; pDFToHtml.WriteCompletionSource.SetResult(true); } // 在调用方等待写入完成后再操作文件 await pdfToHtml.WriteCompletionSource.Task; using (var stream = new FileStream(pdfToHtml.FilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { // 执行文件操作 }
4. 检查文件创建逻辑
确保目标文件在创建时没有被其他隐式的流占用,比如如果之前有创建文件的代码,也要用using包裹流,避免句柄泄漏:
// 示例:创建空文件时确保流释放 using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) { // 写入空内容或初始化 }
额外验证建议
- 测试时可在触发
FileStream前添加短暂延迟(仅用于排查,不建议作为最终方案),确认是否是写入未完成导致的冲突 - 使用Android Studio的Device File Explorer查看目标文件状态,或通过
lsof命令(需root权限)检查文件的占用进程
内容的提问来源于stack exchange,提问作者karanam kalyani

