Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Cargar un PDF de BD (base64) a un TWebBrowser (https://www.clubdelphi.com/foros/showthread.php?t=96762)

darkamerico 27-06-2024 15:43:30

Cargar un PDF de BD (base64) a un TWebBrowser
 
Saludos amigos,
Tengo la siguiente unidad:

Código Delphi [-]
unit uPrincipal;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.StdCtrls, Data.DB, Vcl.Grids, Vcl.DBGrids, System.NetEncoding,
  Vcl.OleCtrls, SHDocVw;

type
  TfrmPrincipal = class(TForm)
    Label1: TLabel;
    txtIDDoc: TEdit;
    Label2: TLabel;
    txtTituloPDF: TEdit;
    Label3: TLabel;
    btnAbrirPDF: TButton;
    open: TOpenDialog;
    Label4: TLabel;
    gridArchivos: TDBGrid;
    web: TWebBrowser;
    Label5: TLabel;
    procedure btnAbrirPDFClick(Sender: TObject);
    procedure gridArchivosCellClick(Column: TColumn);
  private
    { Private declarations }
  public
    function PDFToBase64(const FilePath: string): string;
    procedure ViewPDFInWebBrowser(WebBrowser: TWebBrowser; const Base64Str: string);
  end;

var
  frmPrincipal: TfrmPrincipal;

implementation

uses
  uDM;

{$R *.dfm}

procedure TfrmPrincipal.ViewPDFInWebBrowser(WebBrowser: TWebBrowser; const Base64Str: string);
var
  MemoryStream: TMemoryStream;
  DataUrl: string;
  ByteStream: TStringStream;
  Bytes: TBytes;
begin
  MemoryStream := TMemoryStream.Create;
  Bytes := TEncoding.UTF8.GetBytes(Base64Str);
  MemoryStream.Clear;
  MemoryStream.WriteBuffer(Bytes[0], Length(Bytes));
  MemoryStream.Position := 0;

  try
    ByteStream := TStringStream.Create;
    try
      MemoryStream.Position := 0;
      ByteStream.LoadFromStream(MemoryStream);
      DataUrl := 'data:application/pdf;base64,' + TNetEncoding.Base64.EncodeBytesToString(ByteStream.Bytes);
      WebBrowser.Navigate('about:blank');

      WebBrowser.OleObject.Document.Write(VarArrayOf([DataUrl])); // <-- Error "Los Tipos No Coinciden"
      WebBrowser.Refresh;
    finally
      ByteStream.Free;
    end;
  finally
    MemoryStream.Free;
  end;
end;

function TfrmPrincipal.PDFToBase64(const FilePath: string): string;
var
  FileStream: TFileStream;
  Bytes: TBytes;
begin
  FileStream := TFileStream.Create(FilePath, fmOpenRead or fmShareDenyWrite);
  try
    SetLength(Bytes, FileStream.Size);
    FileStream.ReadBuffer(Bytes[0], FileStream.Size);
    Result := TNetEncoding.Base64.EncodeBytesToString(Bytes);
  finally
    FileStream.Free;
  end;
end;

procedure TfrmPrincipal.btnAbrirPDFClick(Sender: TObject);
var
  insertedOk: boolean;
  Base64PDF: string;
begin
  if open.Execute then
  begin
    Base64PDF := PDFToBase64(open.FileName);
    try
      dm.q_SavePDF.Close;
      dm.q_SavePDF.Params[0].AsInteger := StrToInt(txtIDDoc.Text);
      dm.q_SavePDF.Params[1].AsString := Trim(txtTituloPDF.Text);
      dm.q_SavePDF.Params[2].AsString := Base64PDF;
      dm.q_SavePDF.ExecSQL;
      insertedOk := True;
    except
      insertedOk := False;
    end;
    if not insertedOk then
    begin
      ShowMessage('Error: No se pudo Insertar el Registro');
      Exit;
    end
    else
    begin
      ShowMessage('Ok: Registro Insertado!');

    end;
  end;
end;

procedure TfrmPrincipal.gridArchivosCellClick(Column: TColumn);
begin
  ViewPDFInWebBrowser(web, gridArchivos.DataSource.DataSet.FieldByName('docPDF').AsString);
end;

end.

La grabacion funciona bien, cuando quiero ver el PDF haciendo clic en la grilla me aparece un error en la linea:

Código Delphi [-]
WebBrowser.OleObject.Document.Write(VarArrayOf([DataUrl])); // <-- Error "Los Tipos No Coinciden"

Alguien me daría una mano?

Atte
Americo

darkamerico 27-06-2024 18:22:11

Otra variante
 
Este otra versión de la función, interesante pero también presenta error:

Código Delphi [-]
procedure TfrmPrincipal.ViewPDFInWebBrowser(WebBrowser: TWebBrowser; const Base64Str: string);
var
  PDFBase64: TBase64Encoding;
  PDFDoc: Variant;
  HTMLString: WideString;
  Stream: TStringStream;
begin
  PDFBase64 := TBase64Encoding.Create;
  try
    PDFBase64.Decode(Base64Str);
    PDFDoc := '' +
              '<_embed width="100%" height="100%" type="application/pdf" ' +
              'src="data:application/pdf;base64,' + Base64Str + '">' +
              '';
    HTMLString := PDFDoc;
    Stream := TStringStream.Create(HTMLString, TEncoding.UTF8);
    try
      WebBrowser.Navigate('about:blank');
      while WebBrowser.ReadyState < READYSTATE_INTERACTIVE do
        Application.ProcessMessages;
      (WebBrowser.Document as IPersistStreamInit).Load(TStreamAdapter.Create(Stream));
    finally
      Stream.Free;
    end;
  finally
    PDFBase64.Free;
  end;
end;

pgranados 28-06-2024 05:37:21

Hola, hace mucho yo intente cargar un PDF en un TWebBrowser y por mas que intente no pude, solo pude cargar archivos XML. Lo que termine haciendo fue usar el TWebBrower de TMS para leer PDF.

Ahorita no tengo mi laptop del trabajo pero mañana te paso el codigo de como cargar un PDF en un TWebBrowser de TMS

darkamerico 28-06-2024 14:38:45

Gracias
 
Gracias por responder, quedo atento.

Estuve viendo y tengo instalado el componente TTMSFNCWebBrowser.

Atte

pgranados 28-06-2024 16:37:57

Necesitas uno llamado TAdvWebBrowser y en la forma donde vayas a tener tu visor pdf en el evento onCreate pon esto:

Código Delphi [-]
try
    webBrowser1.Navigate('file:///'+FormaPrincipal.psRutaPDF);
except
    ShellExecute(Handle, 'open', 'explorer.exe', PChar(FormaPrincipal.psRutaPDF), nil, SW_SHOWNORMAL);
    PostMessage(Handle, WM_CLOSE, 0, 0); // cierro la forma
    Exit;
end;

FormaPrincipal.psRutaPDF es una variable tipo string el cual almacena la ruta del archivo. Si en algun caso no se logra cargar el PDF en el webBrowser se abrira el archivo con el visor pdf predeterminado en la computadora.

Recuerda que los WebBrowser basados en Chromiun necesitan una dll llamada WebView2Loader

darkamerico 28-06-2024 16:39:55

Amigo
 
Hola, tengo un problemita con la libreria WevView2Loader_xXX.dll,
Según este manual, baje los instaladores de ambas plataformas x32, x64 pero me dice que YA están instaladas en mi sistema.

darkamerico 28-06-2024 16:40:42

Cita:

Empezado por pgranados (Mensaje 556429)
Necesitas uno llamado TAdvWebBrowser y en la forma donde vayas a tener tu visor pdf en el evento onCreate pon esto:

Código Delphi [-]
try
    webBrowser1.Navigate('file:///'+FormaPrincipal.psRutaPDF);
except
    ShellExecute(Handle, 'open', 'explorer.exe', PChar(FormaPrincipal.psRutaPDF), nil, SW_SHOWNORMAL);
    PostMessage(Handle, WM_CLOSE, 0, 0); // cierro la forma
    Exit;
end;

FormaPrincipal.psRutaPDF es una variable tipo string el cual almacena la ruta del archivo. Si en algun caso no se logra cargar el PDF en el webBrowser se abrira el archivo con el visor pdf predeterminado en la computadora.

Recuerda que los WebBrowser basados en Chromiun necesitan una dll llamada WebView2Loader

Amigo, baje el TMS VCL UI Pack y al tratar de instalarlo, se cruza con TMS Component Pack.

pgranados 28-06-2024 16:43:18

Para eso yo utilizo este codigo:

Código Delphi [-]
if EsWindows64Bits then // Si Windows es 64 bits vamos a mandar las DLL
begin // Si Windows es de 64 bits copio y pego las dos DLL en System 32
    if not FileExists('C:\Windows\SysWOW64\WebView2Loader_x86.dll') then
    begin
      if not FileExists(psRutaRaizServer+'WebView2Loader_x86.dll') then
        exit;

      Destino := 'C:\Windows\SysWOW64';

      ZeroMemory(@FO, SizeOf(FO));
      FO.Wnd := 0;
      FO.wFunc := FO_COPY;
      FO.pFrom := PChar(psRutaRaizServer+'WebView2Loader_x86.dll' + #0);
      FO.pTo := PChar(Destino + #0);
      FO.fFlags := FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR;

      if SHFileOperation(FO) <> 0 then
        Application.MessageBox(PChar('Hubo un error con WebView2Loader_x86.dll'), PChar(csNombrePrograma),MB_OK + MB_ICONERROR)
      else
        lbTerminar:= true;
    end;

    if not FileExists('C:\Windows\SysWOW64\WebView2Loader_x64.dll') then
    begin
      if not FileExists(psRutaRaizServer+'WebView2Loader_x64.dll') then
        exit;

      Destino := 'C:\Windows\SysWOW64';

      ZeroMemory(@FO, SizeOf(FO));
      FO.Wnd := 0;
      FO.wFunc := FO_COPY;
      FO.pFrom := PChar(psRutaRaizServer+'WebView2Loader_x64.dll' + #0);
      FO.pTo := PChar(Destino + #0);
      FO.fFlags := FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR;

      if SHFileOperation(FO) <> 0 then
        Application.MessageBox(PChar('Hubo un error con WebView2Loader_x64.dll'), PChar(csNombrePrograma),MB_OK + MB_ICONERROR)
      else
        lbTerminar:= true;
   end;
end;

if lbTerminar = true then
begin
    Application.MessageBox(PChar('Al cerrar este mensaje se cerrará el sistema. al volver a entrar se terminarán de instalar los archivos requeridos para el correcto funcionamiento de 
    este.'), PChar(csNombrePrograma),MB_OK + MB_ICONWARNING);
    // Aqui cerramos el programa
end;

darkamerico 28-06-2024 16:56:48

Cita:

Empezado por pgranados (Mensaje 556434)
Para eso yo utilizo este codigo:

Código Delphi [-]
if EsWindows64Bits then // Si Windows es 64 bits vamos a mandar las DLL
begin // Si Windows es de 64 bits copio y pego las dos DLL en System 32
    if not FileExists('C:\Windows\SysWOW64\WebView2Loader_x86.dll') then
    begin
      if not FileExists(psRutaRaizServer+'WebView2Loader_x86.dll') then
        exit;

      Destino := 'C:\Windows\SysWOW64';

      ZeroMemory(@FO, SizeOf(FO));
      FO.Wnd := 0;
      FO.wFunc := FO_COPY;
      FO.pFrom := PChar(psRutaRaizServer+'WebView2Loader_x86.dll' + #0);
      FO.pTo := PChar(Destino + #0);
      FO.fFlags := FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR;

      if SHFileOperation(FO) <> 0 then
        Application.MessageBox(PChar('Hubo un error con WebView2Loader_x86.dll'), PChar(csNombrePrograma),MB_OK + MB_ICONERROR)
      else
        lbTerminar:= true;
    end;

    if not FileExists('C:\Windows\SysWOW64\WebView2Loader_x64.dll') then
    begin
      if not FileExists(psRutaRaizServer+'WebView2Loader_x64.dll') then
        exit;

      Destino := 'C:\Windows\SysWOW64';

      ZeroMemory(@FO, SizeOf(FO));
      FO.Wnd := 0;
      FO.wFunc := FO_COPY;
      FO.pFrom := PChar(psRutaRaizServer+'WebView2Loader_x64.dll' + #0);
      FO.pTo := PChar(Destino + #0);
      FO.fFlags := FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR;

      if SHFileOperation(FO) <> 0 then
        Application.MessageBox(PChar('Hubo un error con WebView2Loader_x64.dll'), PChar(csNombrePrograma),MB_OK + MB_ICONERROR)
      else
        lbTerminar:= true;
   end;
end;

if lbTerminar = true then
begin
    Application.MessageBox(PChar('Al cerrar este mensaje se cerrará el sistema. al volver a entrar se terminarán de instalar los archivos requeridos para el correcto funcionamiento de 
    este.'), PChar(csNombrePrograma),MB_OK + MB_ICONWARNING);
    // Aqui cerramos el programa
end;

Brother, el objeto puntero FO, como se define

pgranados 28-06-2024 16:57:41

Código Delphi [-]
FO: TSHFileOpStruct;

darkamerico 28-06-2024 16:59:03

modificaciones a tu codigo
 
Amigo hice algunas cosas en tu codigo:

Código Delphi [-]

function EsWindows64Bits: Boolean;
type
  TIsWow64Process = function(Handle: THandle; var Res: BOOL): BOOL; stdcall;
var
  IsWow64Result: BOOL;
  IsWow64Process: TIsWow64Process;
begin
  Result := False;
  IsWow64Process := GetProcAddress(GetModuleHandle('kernel32.dll'), 'IsWow64Process');
  if Assigned(IsWow64Process) then
  begin
    if IsWow64Process(GetCurrentProcess, IsWow64Result) then
    begin
      Result := IsWow64Result;
    end;
  end;
end;

procedure TfrmPrincipal.Button1Click(Sender: TObject);
var
  rutaSistema, rutaFileLocal, Destino:string;
  FO:TSHFileOpStruct;
  lbTerminar:Boolean;
begin
  rutaFileLocal:=ExtractFilePath(Application.ExeName);
  if EsWindows64Bits then
  begin // Si Windows es de 64 bits copio y pego las dos DLL en System 32
    rutaSistema:='C:\Windows\SysWOW64\';

    if not FileExists(rutaSistema + 'WebView2Loader_x64.dll') then
    begin
      Destino := 'C:\Windows\SysWOW64';

      ZeroMemory(@FO, SizeOf(FO));
      FO.Wnd := 0;
      FO.wFunc := FO_COPY;
      FO.pFrom := PChar(rutaFileLocal+'Files\WebView2Loader_x64.dll' + #0);
      FO.pTo := PChar(Destino + #0);
      FO.fFlags := FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR;

      if SHFileOperation(FO) <> 0 then
        Application.MessageBox(PChar('Hubo un error con WebView2Loader_x64.dll'), PChar('OK'),MB_OK + MB_ICONERROR)
      else
        lbTerminar:=True;
    end;
  end
  else // 32 Bits
  begin
    rutaSistema:='C:\Windows\System32\';

    if not FileExists(rutaSistema+'WebView2Loader_x86.dll') then
    begin
      Destino := 'C:\Windows\System32';

      ZeroMemory(@FO, SizeOf(FO));
      FO.Wnd := 0;
      FO.wFunc := FO_COPY;
      FO.pFrom := PChar(rutaFileLocal+'Files\WebView2Loader_x86.dll' + #0);
      FO.pTo := PChar(Destino + #0);
      FO.fFlags := FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR;

      if SHFileOperation(FO) <> 0 then
        Application.MessageBox(PChar('Hubo un error con WebView2Loader_x86.dll'), PChar('Ok'),MB_OK + MB_ICONERROR)
      else
        lbTerminar:= true;
     end;
  end;
end;

darkamerico 28-06-2024 21:04:31

Ayuda
 
Amigos, aquí comparto el proyecto donde intento recuperar el PDF en base64 desde la BD y al hacer clic en una grilla se debe visualizar el PDF en el componente TAdvWebBrowser. El archivo .BAK esta ahí también

Lo que deseo evitar es que el PDF de la BD se grabe en archivos temporales en el disco.
No es indispensable usar un componente TAdvWebBrowser, con que se visualice el documento con ShellExecute, suficiente.

Proyecto:
Bajar (800k)

Atte,
Americo Torres

pgranados 29-06-2024 01:36:58

Cita:

Empezado por darkamerico (Mensaje 556454)
Lo que deseo evitar es que el PDF de la BD se grabe en archivos temporales en el disco.

Aqui ya no entendi, lo que quieres es guardar un base64 en la base de datos?

Para eso en Firebird 2.5 utilizas el tipo de campo tipo blob no un campo tipo string

pgranados 29-06-2024 01:41:23

Cita:

Empezado por pgranados (Mensaje 556458)
Aqui ya no entendi, lo que quieres es guardar un base64 en la base de datos?

Para eso en Firebird 2.5 utilizas el tipo de campo tipo blob no un campo tipo string

Es mas, directamente guardas el PDF en el BLOB y lo recuperas con un TStream

darkamerico 01-07-2024 17:32:59

Gracias por responder
 
Saludos pgranados,
Como puedes advertir en el código que he mostrado, el archivo PDF ya lo tengo almacenado en base64 en la BD, lo único que deseo ahora es poder visualizarlo.
Mi WhatsApp: +51 956 940 019

Atte
Americo Torres

pgranados 02-07-2024 16:40:28

Estuve investigando un poco el tema y encontre este hilo:


Al parecer Internet Explorer (WebBrowser) utiliza un plugin de Adobe Acrobat para leer PDF en el navegador, puedes intentar seguir el hilo para llegar a la solución pero honestamente te recomiendo usar algun componente de terceros como los TMS o DevExpress o algun componente basado en Chromiun y no en Internet Explorer


La franja horaria es GMT +2. Ahora son las 00:27:50.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi