Ir al contenido


Foto

Como Transferir archivos de un Pc a Otro Utilizando Callback - DataSnap


  • Por favor identifícate para responder
2 respuestas en este tema

#1 genyus00

genyus00

    Advanced Member

  • Miembros
  • PipPipPip
  • 52 mensajes
  • LocationBogota

Escrito 15 mayo 2012 - 04:32

Hola buenas a todos, he querido dejarles un pequeño aporte, de un problema que recientemente resolví, después de tanto buscar y preguntarle a papa google y no encontrar nada, decidí darme a la tarea de buscar solución sin parar.. Bueno:
Mi problema y deseo: uno de mis tantos dilemas en una aplicación de la cual gracias a egostar se saco como un tema aparte http://www.delphiacc...as-con-raudus/ , surgió mi idea de aprovechar las bondades de DataSnap, en principio el proyecto relacionado al link anterior se divide en 3 partes, un servidor de datos DataSnap REST, un servidor DataSnap de calificación y un cliente que consume los dos servidores DataSnap (Datos y calificación).

Hoy en día que ya he terminado dicho proyecto , me surgió la idea de permitir la actualización automática de los archivos del cliente (.exe) por medio de la consulta y comparación de versión de archivos entre el cliente y una carpeta fija en la maquina servidores donde se localiza el Servidor DataSnap de datos.. Intente utilizando Copyfile, CopyfileEx, pero existía el problemas que entre pc me colocaban problema, por un lado que la carpeta no estaba compartida, que la ruta no era valida, eso toco palié como gato boca arriba y nada, hasta que me acorde del webinar al que había asistido vía online, referente al uso de callback (dictado por el amigo andreano lanusse), es mas tengo implementado callback entre mi aplicación Servidora y la Cliente (esta de tipo RAUDUS), en dicho webinar se mostraba el ejemplo de paso de un archivo de imagen vía callback a un servidor, el cliente tenia una progressbar para indicar el progreso del procesamiento de la imagen, esta imagen es recibida por el servidor quien la invierte (pone boca abajo) y la retorna al cliente. El problema de los diversos ejemplos de pasos de archivos es que siempre son imágenes o texto, pero archivos .exe o .dll..

Bueno lo que hice fue darme a la tarea de analizar como era el paso de archivos en dicho ejemplo y ver como adaptarlo para que me sirviera para pasar entre pc archivos de cualquier tipo, en mi caso dll y exe, por medio de la verificación de versión. Así de ese modo para actualizar cada cliente con la ultima versión de cliente y servidor de calificación solo bastaría con colocar los .exe del caso en la carpeta /updates del servidor y listo.

Como Funciona:

1. El cliente finaliza los procesos cliente y calificador
2. Envía nombre de archivo que se desea verificar y versión del mismo al servidor
3. Si existe una versión superior a la verificada en el servidor, este retorna el tamaño en bytes de la nueva versión del archivo.
4. En caso que las versiones no sean la misma retorna el valor de 0 y continua con la verificación del siguiente archivo configurado para verificar.
5. El cliente recibe el tamaño en bytes del archivo a actualizar y hace el llamado via callback hacia el servidor pasándole como parámetro el nombre del archivo por actualizar, versión y un argumento de tipo TDBXCallback (sin este parámetro el servidor no puede conversar con el cliente)
6. En el servidor de datos se lee carga una variable de tipo TMemoryStream con el contenido del archivo por actualizar haciendo un loadfromfile().
7. Unas ves cargado el archivo se inicia el paso por partes hacia el cliente quien lo recibe y lo va almacenando en una variable de tipo TStream, y al tiempo se actualiza la barra de progreso indicado el estado de la actualización. (cabe anotar que desde el cliente se hace el paso de una función de tipo abstracta, que es la encargada de la actualización de la barra de progreso)
8. Al terminar la transaferencia de datos, en el servidor se destruyen las variables creadas y que ocupan memoria y se hacen nulas.
9. En el cliente el archivo recibido desde el servidor queda inicialmente cargado en una variable de tipo TStream,  y para guardarlo como archivo en la maquina local se le es asignado a una segunda variable de tipo TMemoryStream haciendo un loadfromStream(), y luego un savetofile () .. y listo.. ya tenemos nuestro archivo actualiza.. ahora pasemos al código fuente..

SERVIDOR DE DATOS : En la parte del public de la unidad ServerMethods1, agregue dos funciones.



delphi
  1.   public
  2.     { Public declarations }
  3.     function EchoString(Value: string): string;
  4.     function ReverseString(Value: string): string;
  5.  
  6.     //Verifica version de archivo indicado y retorna version del archivo localizada en el servidor
  7.     function RetUpdate_Disponible(Args:tdbxcallback ;FileName,Version: string):TStream;
  8.     //Retonar el tamaño en byte del archivo indicado desde el cliente y si existe una nueva version retorna el tamaño en bytes
  9.     function newupdate_size(FileName,Version:string):Int64;//de la nueva version
  10.    
  11.     procedure TratarExcepciones(DataSet: TDataSet; e:Exception;tabla:string);
  12.   end;



Cuerpo de las funciones declaradas



delphi
  1. function TServerMethods1.newupdate_size(FileName, Version: string): Int64;
  2. var Ruta: String;
  3. NewStream:TMemoryStream;
  4. begin
  5. Ruta:=GetAppVersion(FileName,Version);
  6. if Ruta<>'' then
  7.   try
  8.       NewStream:=TMemoryStream.Create;
  9.       NewStream.LoadFromFile(Ruta);
  10.       Result:=NewStream.Size;
  11.       except
  12.             FreeAndNil(NewStream);
  13.             Result:=0;
  14.             exit;
  15.             end;
  16. end;
  17.  
  18.  
  19. function TServerMethods1.RetUpdate_Disponible(Args: tdbxcallback; FileName,
  20.   Version: string): TStream;
  21. begin
  22. Result:=verifica_version(args,FileName,Version);
  23. end;



En una unidad a la que llame Ufunciones tengo la declaracion de la funcion Verifica_version().



delphi
  1. unit UFunciones
  2.  
  3. Uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  4. Dialogs, ExtCtrls,IniFiles,WedModuleServer, Registry,DBXJSON;
  5.  
  6. //CONSTANTES
  7. //PROCEDIMIENTOS
  8. //FUNCIONES
  9. function GetAppVersion(FileExeName,Version:string):string;
  10. function verifica_version(Args:TDBXCallback;FileName,Version:string):TStream;
  11. //VARIABLES GLOBALES
  12. implementation
  13.  
  14. //*****************************************************************
  15. // Función que retorna la ruta del archivo que debe ser enviado al cliente dado su
  16. // nombre y version, si y solo si la version ingresada es menor que la version del
  17. // mismo archivo ubicado en la carpeta update del servidor
  18. //*****************************************************************
  19. function GetAppVersion(FileExeName,Version:string):string;
  20. var Size, Size2: DWord;
  21. Pt, Pt2: Pointer;
  22. VerUpdate:string;
  23. begin
  24. if not FileExists('.\Updates\'+FileExeName) then
  25.   begin
  26.   result:='';
  27.   exit;
  28.   end;
  29.  
  30. Size := GetFileVersionInfoSize(PChar ('.\Updates\'+FileExeName), Size2);
  31. if Size > 0 then
  32.   begin
  33.   GetMem (Pt, Size);
  34.   try
  35.       GetFileVersionInfo (PChar ('.\Updates\'+FileExeName), 0, Size, Pt);
  36.       VerQueryValue (Pt, '\', Pt2, Size2);
  37.       with TVSFixedFileInfo (Pt2^) do
  38.           begin
  39.           VerUpdate:= IntToStr (HiWord (dwFileVersionMS)) + '.' +
  40.                     IntToStr (LoWord (dwFileVersionMS)) + '.' +
  41.                     IntToStr (HiWord (dwFileVersionLS)) + '.' +
  42.                     IntToStr (LoWord (dwFileVersionLS));
  43.  
  44.           if VerUpdate>Version then
  45.               result:='.\Updates\'+FileExeName
  46.           else
  47.               result:='';
  48.           end;
  49.       finally
  50.             FreeMem (Pt);
  51.             end;
  52.   end;
  53. end;
  54. //*****************************************************************
  55.  
  56. //*****************************************************************
  57. // Función callback que retorna la nueva version archivo solicitado, si y solo si la
  58. // version recibida es menor que la del mismo archivo ubicado en la carpeta /update
  59. // del servidor
  60. //*****************************************************************
  61. function verifica_version(Args:TDBXCallback;FileName,Version:string):TStream;
  62. const MaxBufSize = 25100;
  63. var Ruta: String;
  64. progress:TJSONArray;
  65. clientInstruction:TJSONObject;
  66. doAbort:Boolean;
  67. Buffer:PByte;
  68. inputstream:TMemoryStream;
  69. BytesRead:Integer;
  70. begin
  71. try
  72.   Ruta:=GetAppVersion(FileName,Version);//solicitar verificar si hay una version mas reciente
  73.   if Ruta<>'' then //si se obtiene como respuesta una ruta [dirve]:\[ruta]
  74.       begin// entonces existe una nueva version
  75.       Doabort:=false;//esta varibale se utiliza para indicar si se debe abortar el proceso de update
  76.       Result:=TMemoryStream.Create;//se asigna el valor a retornar como tipo memorystream
  77.       inputstream:=TMemoryStream.Create;//a esta variable se le asignara el archivo localizado en el servidor
  78.       try
  79.         inputstream.LoadFromFile(Ruta);//carga variable con datos del archivo a transmitir
  80.         except
  81.               Showmessage(' Oops.. couldn''t read file!' + #13+ SysErrorMessage(GetLastError));
  82.               exit;
  83.               end;
  84.  
  85.       GetMem(Buffer,MaxBufSize);//se reserva un buffer memoria de tamaño maxbufsize (este valor es una constante, entre mas
  86.       repeat// pequeño el valor mas lenta es la carga de la barra de progreso en el cliente)
  87.           BytesRead:=inputstream.read(Buffer^,MaxBufSize);//se lee la cantidad de byte a enviar
  88.  
  89.           //callback para reportar el progreso del envío de datos
  90.           progress:=TJSONArray.Create;
  91.           progress.AddElement(TJSONString.Create('Sending...'));
  92.           progress.AddElement(TJSONNumber.Create(BytesRead));
  93.           clientInstruction:=args.Execute(progress)as TJSONObject;
  94.  
  95.           try
  96.               if clientInstruction.Get(0).JsonString.Value='Abort' then
  97.                 begin
  98.                 if clientInstruction.Get(0).JsonString.ToString='true' then
  99.                     begin
  100.                     doabort:=true;
  101.                     break;
  102.                     end;
  103.                 end;
  104.           finally
  105.                 clientInstruction.Free;
  106.           end;
  107.           until bytesread <maxbufsize;//se ejecuta mientras existan bytes por enviar
  108.  
  109.       if not doAbort then//si no se aborto la operacion desde el cliente
  110.         begin
  111.         // se informa al cliente que el proceso de envio a termado
  112.         progress:=TJSONArray.Create;
  113.         progress.AddElement(TJSONString.Create('Processing...'));
  114.         progress.AddElement(TJSONNumber.Create(0));
  115.         //todo objeto de tipo TDBXCallback tiene un evento [u]Execute[/u], el cual le permite comunicarse con el cliente.
  116.         Args.Execute(progress).Free;
  117.  
  118.         //archivo fue completamente recibido
  119.         inputstream.Position:=0;
  120.         inputstream.SaveToStream(result);//se asigna el archivo en memoria al stream resultado
  121.         Result.Seek(0,tseekorigin.soBeginning);//y es enviado al cliente
  122.         Result.Position:=0;
  123.         end;
  124.       end;
  125. finally
  126.       end;
  127. end;



CLIENTE ENCARGADO DE LA DESCARGA DE VERSIONES



delphi
  1. unit uFrmActiver;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7.   Dialogs, StdCtrls, ComCtrls, UFunciones, DBXDataSnap, DBXCommon, DSHTTPLayer,
  8.   DB, SqlExpr, USvrServer, UDatamodulo, ExtCtrls, JvComponentBase, JvTrayIcon,
  9.   JvBaseDlg, JvWinDialogs, ImgList, Menus, AppEvnts, ButtonComps, Mask,IniFiles,
  10.   JvExMask, JvSpin, AdvGlassButton, AdvGroupBox, AdvSmoothButton, jpeg,zlib,DBXJSON,
  11.   dbxdbreaders;
  12.  
  13. type
  14.   TupdateBar = reference to function (labelname:string; percentcomplete:integer):Boolean;
  15.  
  16.   TProgressBarCallback = class(TDBXCallback)
  17.   private
  18.         updatemethod:tupdateBar;
  19.   Public
  20.         constructor create(LupdateMethod:tupdateBar);
  21.         function execute (const Args: Tjsonvalue):tjsonvalue;override;
  22.   end;
  23.  
  24. TFrmActiver = class(TForm)
  25.     Edit1: TEdit;
  26.     InvokeButton: TAdvGlassButton;
  27.     BTAbort: TAdvGlassButton;
  28.     PB1: TProgressBar;
  29.     LBMsg: TLabel;
  30.     LBStatus: TLabel;
  31.  
  32.     procedure BTAbortClick(Sender: TObject);
  33.     procedure InvokeButtonClick(Sender: TObject);
  34.     procedure FormCreate(Sender: TObject);
  35.  
  36.   private
  37.     { Private declarations }
  38.     IsProcessing, DoAbort:Boolean;
  39.     function CopyLargeStream(source:tstream):tstream;
  40.  
  41.   public
  42.     { Public declarations }
  43.       constructor Create(AOwner: TComponent); override;
  44.       destructor Destroy; override;
  45.   end;
  46.  
  47. var
  48.   FrmActiver: TFrmActiver;
  49.  
  50. implementation
  51.  
  52. {$R *.dfm}
  53.  
  54. function TFrmActiver.CopyLargeStream(source: tstream): tstream;
  55. var BytesRead:integer;
  56. Buffer:PByte;
  57. Const MaxBufSize =$F000;
  58. begin
  59. try
  60.   Result := TMemoryStream.Create;
  61.   Source.Seek(0, TSeekOrigin.soBeginning);
  62.   Source.Position := 0;
  63.   GetMem(Buffer, MaxBufSize);
  64.   repeat
  65.         BytesRead := Source.Read(Buffer^, MaxBufSize);
  66.         if BytesRead > 0 then
  67.             Result.WriteBuffer(Buffer^, BytesRead);
  68.         until BytesRead < MaxBufSize;
  69.   Result.Seek(0, TSeekOrigin.soBeginning);
  70.   finally
  71.   end;
  72. end;
  73.  
  74. procedure TFrmActiver.FormCreate(Sender: TObject);
  75. begin
  76. inherited;
  77. PB1.Min := 0;
  78. PB1.Max := 100;
  79. end;
  80.  
  81. constructor TfrmActiver.Create(AOwner: TComponent);
  82. var Wpuerto,CPuerto,WHost,CHost :string;
  83. begin
  84. inherited;
  85. if Myinifile<>nil then
  86.   begin
  87.   Edit_DNsServer.text:=MyIniFile.ReadString('CONFIGSERV','DNS',DNSlocal);
  88.   Edit_HTTPServer.text:=MyIniFile.ReadString('CONFIGSERV','HTTP','10010');
  89.   end;
  90.  
  91. with Conex do
  92.     begin
  93.     if SERVERCONEX.Connected then
  94.         splay.BackGroundSymbol:=bsPause
  95.     else
  96.         splay.BackGroundSymbol:=bsPlay;
  97.  
  98.     Edit_DNsServer.Enabled:=not SERVERCONEX.Connected;
  99.     Edit_HTTPServer.Enabled:=not SERVERCONEX.Connected;
  100.     Btn_DNsServer.Enabled:=not SERVERCONEX.Connected;
  101.     InvokeButton.Enabled:=(Conex.SERVERCONEX.Connected);
  102.     end;
  103.  
  104. try
  105.   PonerProgramaInicio;
  106.   except
  107.         begin
  108.         application.MessageBox('La aplicacion no puede ser activada para iniciar automáticamente con Windows. Verifique  si tiene permisos para escribir en el registro del sistema.','Activar Inicio con Windows',MB_ICONEXCLAMATION);
  109.         exit;
  110.         end;
  111.     end;
  112. end;
  113.  
  114. destructor TFrmActiver.Destroy;
  115. begin
  116.   inherited;
  117. end;
  118.  
  119. procedure TFrmActiver.InvokeButtonClick(Sender: TObject);
  120. var FileName,version:string;
  121. SQLServerMethod1:TSqlServerMethod;
  122. Proxyclient:TServerMethods1Client;
  123. UpdateProc:TUpdateBar;
  124. MyFileStream:TMemoryStream;
  125. ProccessedStream, TmpStream: TStream;
  126. NewFileSize,BytesSend:int64;
  127. begin
  128. inherited;
  129. KillTask('DQ Cliente.exe');//cerrar proceso
  130. KillTask('DQ Calificador.exe');//cerrar proceso
  131.  
  132. //guardar parametros de conexion al servidor
  133. if Myinifile=nil then
  134.   MyIniFile := TiniFile.Create('.\DBConex.params');
  135.  
  136. MyIniFile.WriteString('CONFIGSERV','DNS',Edit_DNsServer.Text);
  137. MyIniFile.WriteString('CONFIGSERV','HTTP',Edit_HTTPServer.Text);
  138.  
  139. if Conex.SERVERCONEX.Connected then// si hay conexion
  140.   begin
  141.   FileName:='DQ Calificador.exe';
  142.   version:=GetAppVersion('.\DQ Calificador.exe');//obtener version
  143.  
  144.   //declaracion de funcion abstracta que se pasara por callaback al servidor.
  145.   //Esta funcion se dispara en respuesta a los datos recibidos desde el servidor
  146.   //indicando el estado del progreso
  147.   UpdateProc := Function(l:string; a:Integer):boolean
  148.                 begin
  149.                 LBStatus.caption:=l;
  150.                 if l = 'Processing...' then
  151.                     BTAbort.enabled:=false
  152.                 else
  153.                     begin
  154.                     BTAbort.Enabled:=true;
  155.                     BytesSend:=BytesSend+a;
  156.                     PB1.Position:=(Trunc(100*(BytesSend/NewFileSize)));
  157.                     end;
  158.                 Application.ProcessMessages;
  159.                 result:=DoAbort;
  160.                 end;
  161.  
  162.   Proxyclient:=nil;
  163.   MyFileStream:=TMemoryStream.Create;
  164.   proccessedStream:=nil;
  165.  
  166.   try
  167.       IsProcessing:=true;
  168.       InvokeButton.Enabled:=false;
  169.       DoAbort:=false;
  170.       BytesSend:=0;
  171.       PB1.Position:=0;
  172.       Application.ProcessMessages;
  173.       MyFileStream.LoadFromFile(Edit1.Text);
  174.  
  175.       if not DoAbort then
  176.         begin
  177.         Proxyclient:=TServerMethods1Client.Create(Conex.SERVERCONEX.DBXConnection);
  178.         NewFileSize:=Proxyclient.newupdate_size(FileName,version);
  179.         Assert(NewFileSize > 0, 'file size error');
  180.         LBMsg.caption:='';
  181.         LBStatus.caption:='Sending...';
  182.         ProccessedStream:=copylargestream(Proxyclient.retUpdate_Disponible(tprogressBarCallback.create(UpdateProc),FileName,version));
  183.         LBStatus.Caption:='Loading File...';
  184.         Application.ProcessMessages;
  185.         end;
  186.  
  187.       if not DoAbort then
  188.         begin
  189.         myfilestream.LoadFromStream(ProccessedStream);
  190.         myfilestream.SaveToFile('.\DQ Calificador.exe');
  191.         end;
  192.  
  193.       finally
  194.             LBStatus.Caption := 'IDLE';
  195.             IsProcessing := False;
  196.             InvokeButton.Enabled := True;
  197.  
  198.             if DoAbort then
  199.               begin
  200.               LBMsg.Caption := 'Process aborted';
  201.               LBMsg.Font.Color := clFuchsia
  202.               end;
  203.  
  204.             if Assigned(ProccessedStream) then
  205.               FreeAndNil(ProccessedStream);
  206.  
  207.             if Assigned(MyFileStream) then
  208.               FreeAndNil(MyFileStream);
  209.  
  210.             if Assigned(ProxyClient) then
  211.               ProxyClient.Free;
  212.             end;
  213.   end;
  214.  
  215. if FileExists('DQ Calificador.exe') then
  216.   WinExec(PansiChar('DQ Calificador.exe'),SW_SHOWNORMAL);
  217. end;
  218.  
  219. procedure TFrmActiver.BTAbortClick(Sender: TObject);
  220. begin
  221. inherited;
  222. if IsProcessing then
  223.   begin
  224.   DoAbort := True;
  225.   LBStatus.Caption := 'Aborting...';
  226.   Application.ProcessMessages;
  227.   end;
  228.  
  229. InvokeButton.Enabled := True;
  230. end;



Y listo... Quedo atento a sus dudas y preguntas anexo imagen de la ventana de la aplicación que se encarga de solicitar actualizacion al servidor.

Imagen Enviada

Nota: Solo me hace falta el maquillaje e incluir parámetros de configuración como por ejemplo, cada cuando tiempo verifica si hay una nueva versión y cosas asi.

, gracias por tomarse el tiempo de leer este tema..

:cool:  :angel:
  • 0

#2 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 16 mayo 2012 - 08:23

Muy buen aporte amigo.
Muchas gracias. (y) (y) (y)
  • 0

#3 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.446 mensajes
  • LocationMéxico

Escrito 16 mayo 2012 - 08:45

Muchas gracias amigo genyus00

Excelente aporte, en todos los años que he estado en los foros hay muchas preguntas acerca de la actualización en línea y puedo asegurar que éste hilo va a ser un referente obligado. (y)

Saludos

  • 0




IP.Board spam blocked by CleanTalk.