Delphi XE7中TWebBrowser存HTML后文件被占用无法写入Blob字段
Delphi XE7中TWebBrowser保存HTML后文件被占用及LoadFromStream使用问题
问题场景
在Delphi XE7环境下,通过自定义WB_SaveAs_HTML函数将TWebBrowser内容保存为本地HTML文件,随后执行LoadFromFile将文件加载到数据库Blob字段时,触发错误:
EFOpen Error cannot open file ... The process cannot access the file "
" because it is being used by another process
原保存函数代码:
function WB_SaveAs_HTML(WB:TWebBrowser; const FileName : string):boolean; var PersistStream: IPersistStreamInit; Stream: IStream; FileStream: TFileStream; begin result:=true; if not Assigned(WB.Document) then begin result:=false;//ShowMessage('Document not loaded!'); Exit; end; PersistStream := WB.Document as IPersistStreamInit; FileStream := TFileStream.Create(FileName, fmCreate or fmShareDenyNone); try Stream := TStreamAdapter.Create(FileStream, soReference) as IStream; if Failed(PersistStream.Save(Stream, True)) then result:=false //ShowMessage('SaveAs HTML fail!'); finally FileStream.Free; end; (WebBrowser1.Document as IHTMLDocument2).designMode := 'off'; end;
加载Blob字段代码:
(Query.FindField('content') as TBlobField).LoadFromFile(AFileName);
问题原因及解决方案
原因分析
问题出在TStreamAdapter的引用模式:使用soReference时,COM对象会持有TFileStream的引用,导致FileStream.Free执行后,文件句柄并未立即释放(COM引用计数未降为0),后续LoadFromFile无法打开文件。
解决方式1:修改TStreamAdapter所有权模式
将soReference改为soOwned,让TStreamAdapter接管TFileStream的所有权,COM流释放时会自动清理文件流,确保句柄及时关闭。修改后的函数:
function WB_SaveAs_HTML(WB:TWebBrowser; const FileName : string):boolean; var PersistStream: IPersistStreamInit; Stream: IStream; FileStream: TFileStream; begin result:=true; if not Assigned(WB.Document) then begin result:=false; Exit; end; PersistStream := WB.Document as IPersistStreamInit; FileStream := TFileStream.Create(FileName, fmCreate or fmShareDenyNone); try // 使用soOwned让TStreamAdapter接管FileStream所有权 Stream := TStreamAdapter.Create(FileStream, soOwned) as IStream; if Failed(PersistStream.Save(Stream, True)) then result:=false; finally // 注释掉手动Free,由TStreamAdapter负责释放FileStream // FileStream.Free; end; (WB.Document as IHTMLDocument2).designMode := 'off'; end;
解决方式2:手动释放COM流对象
保留soReference,但在保存完成后手动将Stream置为nil,强制触发COM引用计数清理,释放文件流的引用:
function WB_SaveAs_HTML(WB:TWebBrowser; const FileName : string):boolean; var PersistStream: IPersistStreamInit; Stream: IStream; FileStream: TFileStream; begin result:=true; if not Assigned(WB.Document) then begin result:=false; Exit; end; PersistStream := WB.Document as IPersistStreamInit; FileStream := TFileStream.Create(FileName, fmCreate or fmShareDenyNone); try Stream := TStreamAdapter.Create(FileStream, soReference) as IStream; if Failed(PersistStream.Save(Stream, True)) then result:=false; Stream := nil; // 手动释放COM流,触发引用计数清理 finally FileStream.Free; end; (WB.Document as IHTMLDocument2).designMode := 'off'; end;
Query组件LoadFromStream方法使用示例
更高效的方式是跳过本地文件,直接将TWebBrowser内容写入内存流,再加载到Blob字段,彻底避免文件占用问题:
procedure SaveWBContentToBlob(WB: TWebBrowser; Query: TQuery); var PersistStream: IPersistStreamInit; Stream: IStream; MemStream: TMemoryStream; BlobField: TBlobField; begin if not Assigned(WB.Document) then Exit; PersistStream := WB.Document as IPersistStreamInit; MemStream := TMemoryStream.Create; try Stream := TStreamAdapter.Create(MemStream, soOwned) as IStream; if Succeeded(PersistStream.Save(Stream, True)) then begin MemStream.Position := 0; // 重置流指针到起始位置 BlobField := Query.FindField('content') as TBlobField; BlobField.LoadFromStream(MemStream); end; (WB.Document as IHTMLDocument2).designMode := 'off'; finally // soOwned模式下,Stream释放时会自动清理MemStream end; end;
内容的提问来源于stack exchange,提问作者Jacek U
相关产品推荐
相关产品推荐

