Ver Mensaje Individual
  #17  
Antiguo 29-02-2012
Aldo Aldo is offline
Miembro
 
Registrado: ene 2004
Posts: 46
Reputación: 0
Aldo Va por buen camino
Hola a todos. Como os había prometido, aquí tengo la solución al problema que he planteado y que os agradezco la atención que habéis prestado, pero me he decantado por esta solución.

1. En el lado cliente. En el procedimiento que se encarga de salvar los datos.

Código Delphi [-]
{ ATENCION: Las palabras en mayúsculas suelen ser constantes que definen los nombres de los campos y parámetros que se utilizan en cada uno de los 
procedimientos y funciones de la aplicación. Están declaradas en una unit aparte y no creo que sea relevante especificar aquí. Se entiene perfectamente }

var
   szFileName : String;
begin
   ...    
   With MyClientDataSet do
   begin
       ...
      { Asignar a la variable, el nombre del fichero que se debe guardar en el lado servidor  }
      szFileName := ‘Nombre de Fichero’;
      Params.ParamByName( QADDUPD_FILIACIONES_DOCUMENTACIONES_VFICHEROASSTREAM ).LoadFromFile( szFileName, ftBlob );
      ...   
   end; 
end;

2. En el lado capa de negocio hay más de un evento que hay que programar.

Código Delphi [-]

{ ATENCION: La función GetDatabaseNameFromSQLConnection() se encarga de devolver la ruta de la base de datos.
  Por razones obvias no la pongo aquí. 
  Se asume que la ruta final donde se guardarán los ficheros traidos desde los clientes será:
  DATABASE_PATH \ DOCUMENTACION_DIR \ ID_FILIACION   
}

{*******************************************************************************
* sqlQAddFilDocBeforeOpen: Evento OnBeforeOpen del query sqlQAddFilDoc que se
*                          ejecuta justo para crear el fichero que es pasado en el
*                          parámetro QADDUPD_FILIACIONES_DOCUMENTACIONES_VFICHEROASSTREAM
*                          del Query
********************************************************************************}
procedure TMyDataModule.sqlQAddFilDocBeforeOpen( DataSet: TDataSet);
var
   Param           : TParam;
   szFileName      : String;
   nId_Filiacion   : Integer;
   szIdentificador : String;
   Stream          : TStream;
begin
   with TSQLQuery( DataSet ) do
   begin
      { Obtener Ruta de la base de datos así como datos necesarios para definir la ruta  }
      nId_Filiacion   := Params.FindParam( QADDUPD_FILIACIONES_DOCUMENTACIONES_VID_FILIACION ).AsInteger;
      szIdentificador := Params.FindParam( QADDUPD_FILIACIONES_DOCUMENTACIONES_VIDENTIFICADOR ).AsString;
      szFileName      := ExtractFilePath( GetDatabaseNameFromSQLConnection() ) + DOCUMENTACION_DIR +  '\' + IntToStr( nId_Filiacion ) + '\' + szIdentificador;

      { Intentar crear las rutas de directorios para poder grabar el fichero }
      if ForceDirectories( ExtractFileDir( szFileName ) ) then
         begin
            { Buscar el parámetro que trae el fichero a guardar }
            Param := Params.FindParam( QADDUPD_FILIACIONES_DOCUMENTACIONES_VFICHEROASSTREAM );
            { Si lo encuentra }
            if Param <> Nil then
               begin
                  { Si se ha pasado algún fichero en el parámetro}
                  if not Param.IsNull then
                     begin
                        { Crear fichero }
                        Stream := TFileStream.Create( szFileName, fmCreate );
                        try
                           { Salvar fichero a disco }
                           Stream.CopyFrom( Param.AsStream, Param.AsStream.Size );
                        finally
                           Stream.Free;

                           { Guardar el nombre del último fichero creado, se usa en
                             otros eventos }
                           szLastFileName := szFileName;
                        end;
                     end;

                  { Elimina este parámetro de la colección de Params del Query
                    para que en el momento de ejecutar el S.P. del query, no dé
                    error, porque el S.P. no tiene este parámetro }
                  Params.RemoveParam( Param );
               end;
         end;
   end;
end;

{*******************************************************************************
* sqlQAddFilDocAfterOpen: Evento OnAfterOpen del Query sqlQAddFilDoc que se ejecuta
*                         para eliminar el fichero que se pudo haber creado, pero que no hay 
*                         que dejarlo allí, porque ha ocurrido un error en el proceso de creación
********************************************************************************}
procedure TMyDataModule.sqlQAddFilDocAfterOpen( DataSet: TDataSet);
begin
   { Si hubo error en el P.A. de crear una fila en la tabla de Filiaciones
   Documentaciones }
   if TSQLQuery( DataSet ).Fields.FindField( VRESULT ).AsInteger <= 0 then
      { Si el fichero guardado en la variable szLastFileName ( toma valor en el
       evento sqlQAddFilDocBeforeOpen ) existe, eliminarlo  }
      if FileExists( szLastFileName ) then
         begin
            SysUtils.DeleteFile( szLastFileName );

            { Intenta eliminar el directorio en el que estaba el fichero eliminado,
              si éste está vacío }
            RemoveDir( ExtractFilePath( szLastFileName ) );
         end;

   { Limpia la variable para otra ejecución }
   szLastFileName := '';
end;

Como conclusión final os diré que también programé una función par a una UDF que se ejecuta desde los triggers de AFTER DELETE y AFTER UPDATE de la tabla donde se almacena la referencia de los ficheros contenidos en el catálogo de documentos. Estos triggers se ejecutan para mantener la integridad referencial entre los registros de esta tabla en la base de datos y los ficheros físicos guardados en el disco duro del Servidor. Donde:

1. El caso del trigger AFTER DELETE, se ejecuta por razones obvias que debe eliminar el fichero del registro que se ha eliminado de esta tabla.

2. en el caso del trigger AFTER UPDATE, se ejecuta cuando se ha cambiado el nombre de un fichero por otro ( esto incluye no solo el nombre sino también su contenido ). En este caso se elimina el viejo fichero y solo queda guardado en el catálogo de documentos el nuevo fichero.


Por otro lado. Os explico que me decidí por esta variante, por todas las cosas que antes se han debatido en este hilo, más las condiciones reales de implantación de esta aplicación en el cliente final. Donde he tenido en cuenta lo siguiente:

1. No puedo contar con la base da datos sobre UNIX como me sugeristes, porque esto encarecería el proyecto, obligando al cliente final tener más de un servidor con distintos sistemas operativos, ya que de cualquier forma este cliente necesita el Windows en el lado Servidor, para que se ejecute también allí mi aplicación de la capa de negocios y otras aplicaciones que se ejecutan sobre windows.

2. El tamaño de la base de datos es algo a tener en cuenta sobre todo en los procesos de backups y restores.

3. El sistema propuesto por olbeup no me convence del todo, porque no me gustaría tener tantas bases de datos como pacientes hayan registrados en la aplicación. Además de que creo que la solución que ha propuesto no se refiere a bases de datos Firebird. Si es el caso, entonces soy yo el que no conoce esa nueva forma de referirte a otras bases de datos desde la base de datos actual. En resumen, el caso de '.dbo.' creo que es algo que se usa en las bases de datos de SQL Server y alguna otra, pero no en Firebird.

4. Por último y no menos importante, era mantener la transparencia de localización de los archivos en el Servidor sin tener que estar compartiendo directorios en la red para poder acceder a ellos y poder guardar y recuperar los ficheros de dichos directorios. Además de los inconvenientes que existen en windows con todo el sistema de Seguridad al compartir directorios en la Red.


Luego queda algo referente a la visualización de los documentos cada vez que el cliente así lo requiera. En ese caso me decanté por tener una tabla en memoria que solo contiene un registro con un camopo Blob en el que se almacena el fichero que es recuperado de su lugar físico del disco duro del servidor. Para ser mostrados en el lado cliente se trae una copia de dicho fichero y se visualiza con la función del API ShellExecute.

Aquí queda una cuestión interesante a tener en cuenta y es si utilizando la función Shellexecute se puede abrir dicho fichero en modo de solo Lectura, para que no pueda ser modificado por ningún cliente de la aplicación una vez que ya está guardado en el catálogo de documentos.

Tengo una vaga idea y es cambiarle los atributos al fichero y ponerle 'De solo lectura', pero no sé como se comportará cuando el fichero sea abierto por su aplicación. ¿ Alguna idea al respecto ?

Espero os haya servido de algo. Además si alguien encuentra algún fallo os agradezco que me lo hagan saber y así corregir y/o perfeccionar este planteamiento.
Responder Con Cita