You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Delphi Seattle中将内存中的PDF加载到TWebBrowser组件?

在TWebBrowser中直接显示内存中的PDF(无需保存到文件)

我之前也碰到过类似的问题,自定义协议处理PDF失效通常是因为响应头设置不全或者IE插件的兼容性问题。下面给你几个经过验证的可行方案:

方法1:利用IWebBrowser2直接加载IStream

这是兼容性最好的方式,核心是把内存中的PDF流包装成COM的IStream接口,再传给TWebBrowser的Navigate2方法:

uses
  ActiveX, SHDocVw, Classes;

procedure LoadPDFFromStream(WebBrowser: TWebBrowser; PDFStream: TStream);
var
  StreamAdapter: IStream;
  WebBrowserIntf: IWebBrowser2;
  EmptyVar: OleVariant;
begin
  if not Assigned(PDFStream) or (PDFStream.Size = 0) then Exit;

  PDFStream.Position := 0;
  // 将Delphi流包装成COM标准IStream
  StreamAdapter := TStreamAdapter.Create(PDFStream, soReference);
  WebBrowserIntf := WebBrowser.DefaultInterface;

  // 先导航到空白页确保浏览器处于就绪状态
  EmptyVar := EmptyParam;
  WebBrowserIntf.Navigate2('', @EmptyVar, @EmptyVar, @EmptyVar, @EmptyVar);
  while WebBrowserIntf.ReadyState <> READYSTATE_COMPLETE do
    Application.ProcessMessages;

  // 加载内存中的PDF流
  WebBrowserIntf.Navigate2('', @EmptyVar, @EmptyVar, StreamAdapter, @EmptyVar);
end;

注意事项:

  • 确保系统安装了Adobe Reader或其他支持在IE中渲染PDF的插件;
  • 如果是Delphi 10.4及以上版本,建议将TWebBrowser设置为使用Edge内核,兼容性和稳定性会大幅提升。

方法2:将PDF转为Base64嵌入HTML

这种方式无需依赖复杂的COM接口,直接通过HTML的embed标签加载Base64编码的PDF:

uses
  ActiveX, SHDocVw, Classes, MSHTML, System.NetEncoding;

procedure LoadPDFAsBase64(WebBrowser: TWebBrowser; PDFStream: TStream);
var
  Base64Content: string;
  HtmlDoc: IHTMLDocument2;
  HtmlContent: string;
begin
  if not Assigned(PDFStream) or (PDFStream.Size = 0) then Exit;

  PDFStream.Position := 0;
  // 将PDF二进制流转为Base64字符串
  Base64Content := TNetEncoding.Base64.EncodeBytesToString(TBytesStream(PDFStream).Bytes);

  // 先导航到空白页
  WebBrowser.Navigate('about:blank');
  while WebBrowser.ReadyState <> READYSTATE_COMPLETE do
    Application.ProcessMessages;

  // 获取文档对象并写入嵌入PDF的HTML
  HtmlDoc := WebBrowser.Document as IHTMLDocument2;
  HtmlContent := Format(
    '<html><body style="margin:0;"><embed type="application/pdf" src="data:application/pdf;base64,%s" width="100%%" height="100%%"></body></html>',
    [Base64Content]
  );
  HtmlDoc.Write(HtmlContent);
  HtmlDoc.Close;
end;

注意事项:

  • 该方法对IE11及以上版本支持较好,旧版IE可能无法识别data: URI方案;
  • 大体积PDF的Base64编码会额外占用约30%的内存,需注意内存开销。

方法3:修复自定义协议处理逻辑

如果你想继续使用自定义协议,需要确保协议处理程序正确返回响应头和PDF数据:

uses
  ActiveX, WinInet;

function TCustomProtocolHandler.Start(const szUrl: PWideChar;
  IInternetSink: IInternetSink; dwFlags: DWORD; out dwResult: DWORD): HResult;
var
  PDFStream: TMemoryStream;
  ResponseHeaders: string;
  BytesWritten: Cardinal;
begin
  // 替换成你获取内存PDF流的逻辑
  PDFStream := TMemoryStream.Create;
  try
    // 假设已将PDF数据加载到PDFStream中
    PDFStream.Position := 0;

    // 必须设置完整的响应头:Content-Type和Content-Length缺一不可
    ResponseHeaders := Format(
      'Content-Type: application/pdf'#13#10 +
      'Content-Length: %d'#13#10#13#10,
      [PDFStream.Size]
    );

    // 发送响应头
    IInternetSink.ReportData(BSCF_FIRSTDATANOTIFICATION or BSCF_LASTDATANOTIFICATION, Length(ResponseHeaders), 0);
    IInternetSink.Write(Pointer(ResponseHeaders), Length(ResponseHeaders), @BytesWritten);

    // 发送PDF二进制数据
    IInternetSink.ReportData(BSCF_FIRSTDATANOTIFICATION or BSCF_LASTDATANOTIFICATION, PDFStream.Size, 0);
    IInternetSink.Write(PDFStream.Memory, PDFStream.Size, @BytesWritten);

    dwResult := 0;
    Result := S_OK;
  finally
    PDFStream.Free;
  end;
end;

关键修复点:

  • 必须返回Content-Length头,否则IE可能无法正确解析完整的PDF数据;
  • 响应头和数据之间必须用一个空行(#13#10#13#10)分隔;
  • 检查IE安全设置,确保自定义协议被允许加载ActiveX内容。

内容的提问来源于stack exchange,提问作者Konstantin Knyazev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:06:46