Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Conexión con bases de datos (https://www.clubdelphi.com/foros/forumdisplay.php?f=2)
-   -   Compatibilidad de imagenes en campos Blob (https://www.clubdelphi.com/foros/showthread.php?t=79605)

roman 24-07-2012 21:01:44

Cita:

Empezado por Al González
mira lo que hace TPicture

Pues justamente, se asegura de destruir el objeto Graphic que hubiera antes, o ¿a qué te refieres? En el resto de observaciones estoy de acuerdo.

// Saludos

Al González 24-07-2012 23:58:10

Hola Román. :)

Cita:

Empezado por Al González (Mensaje 438101)
[...] con el desafortunado precio de nunca destruir los objetos TJPEGImage y TBitMap que asignas (copias) a Image1.Picture.Graphic [...]
Código Delphi [-]
procedure TPicture.SetGraphic(Value: TGraphic);
var
  NewGraphic: TGraphic;
begin
  NewGraphic := nil;
  if Value <> nil then
  begin
    NewGraphic := TGraphicClass(Value.ClassType).Create;
    NewGraphic.Assign(Value);
    ...

Es que, como puedes ver, SetGraphic es el método de escritura de la propiedad Graphic. El objeto asignado a dicha propiedad es copiado (con el método Assign que ahí se ve) a NewGraphic / FGraphic (otro objeto).

En el código de ElMug:
Código Delphi [-]
      
      Image1.Picture.Graphic:= TJpegImage.Create; {assume is Jpeg}
      ...
        Image1.Picture.Graphic:= TBitMap.Create; {bitmap}
se crean dos objetos "al vuelo" que luego no son destruidos. Se quedan en la memoria esas instancias TJPEGImage y TBitMap.

Seguro es que no te iba a resultar nada difícil dar con ello, pero creo que no estuvo de más aclararlo.

Cambiando de tema, que bueno que regresaste. ¿Ya podemos irnos de vacaciones el resto de los milenarios? Mira que fue bastante arduo intentar cubrirte estas semanas. ;)

Un abrazo.

Al.

roman 25-07-2012 06:52:10

Sí. Tienes razón.

// Saludos

ElMug 25-07-2012 08:03:06

Estimado Al Gonzalez,

Gracias por el interes y comentar.

Estas son mis observaciones a ellos:

1. Asumes que "Image1.Picture.Graphic:= TJpegImage.Create" no va a causar ningún problema, por lo que BS podría quedar sin destruirse nunca.
...............................
No. No causa problemas, porque "Image1.Picture.Graphic:= TJpegImage.Create" solo crea una INSTANCIA, prepara memoria, e inicializa propiedades. Es uso trivial, ver documentacion.

2. Asumes que si falla el primer LoadFromStream, es definitivamente por no tratarse de una imagen JPEG. ¿Será la única razón por la cual pueda elevarse una excepción al ejecutar esa sentencia?
...............................
En este comentario, no serias tu el que asume que asumo, pues de antemano es sabido que los Try detectan cualquier tipo de factor que le cause falla? A tu pregunta "Sera la unica razon?" la respuesta es obvia. NO. No se piensa que sea la unica razon. Pero LA RAZON de interes es que va a fallar si en el Blob no esta guardado un JPEG. Y lo mismo cuando se hace Try para Bitmap.


3. No hay ninguna garantía de que el objeto BS sea destruido si falla el segundo LoadFromStream. OK, todos hacemos pequeñas asunciones en nuestro código de cuando en cuando, ¿pero qué tal si aun siendo una imagen BMP, LoadFromStream tuviera dificultades para leerla?
....................
Si hay otra dificultad, el Try se encarga de marcar error. Eso es elemental. En el caso de un archivo defectuoso, el Try al final levanta un dialogo. El programador conocedor del uso del Try le puede agregar mas codigo, inclusive para que no levante dialogo, y el haga sus codigo con determinaciones especificas.

4. La no liberación de los objetos TGraphic que creas.
.....................
El codigo que muestro no se liberan objetos porque hay usos particulares que podrian requerir seguir usando el objeto. La destruccion de objetos creados la considero cuestion particular de la aplicacion. La creacion, que SI es necesaria, es lo que muestro.


Es lo que se puede notar en tu solución a simple vista. Es una mala práctica emplear "excepciones controladas" para determinar el flujo del programa; para tomar decisiones están los Ifs no los Excepts. ¿Que te ahorras campos? Muy bien, PERO siempre que no dejes tu aplicación llena de pequeñas trampas.
...................
No. Todo uso de recurso que facilite la plataforma de desarrollo no es ninguna trampa. Cuando funciona y no se puede demostrar que no funciona, lo que no es apropiado es juzgar en base en ataduras dogmaticas al pasado o a lo "convencional". Ademas, como seguramente sabes, los "ifs" no detectan errores de este tipo. Los "ifs" se usarian si el metodo es escudriñando el Blob en sus bits o bytes. Pero precisamente, porqe mi procedimiento no lo escudriña, los "ifs" NO se pueden usar y NO se usan. De hecho, base del procedimiento ES el uso de los Try's, y el uso de Try's no es ninguna trampa. Que a algunos les parezca "trampa" no creo poder evitarlo.

En cuanto al empleo de objetos auxiliares dentro de las rutinas, ya sabes la regla:
Código:


1. Apertura (creación)
Try
2. Uso
Finally
3. Cierre (destrucción)

...............
Esto es elemental. No se muestra destruccion de objetos creados. La aplicacion puede darle mas uso al objeto creado, y si no lo requiere, se encargue de destruirlo. En otras palabras, el procedimiento trabaja sin destruirlos. La creacion de esos objetos es necesaria, y se muestra. Destruirlos es opcional y en X caso aplicado, es particular, y funcion del programador.

Espero que esto module tus concernimientos y veas que lo compartido si puede ser de utilidad, si no a algunos, a otros si.

roman 25-07-2012 09:24:47

A lo que Al se refiere en el punto 1 es a que la mera asignación:

Código Delphi [-]
Image1.Picture.Graphic:= TJpegImage.Create;

podría causar una excepción, por ejemplo, por falta de memoria o alguna otra razón imprevisible, como son las excepciones. Si ese fuera el caso, dado que tal excepción no está protegida por un bloque try-except, el código donde liberas a BS (creado anteriormente) no llegaría a ejecutarse y tendrías una fuga de memoria.

Por otra parte, estoy de acuerdo en que un objeto no necesariamente debe destruirse en el mismo contexto en donde se creó ya que posteriormente puede usarse. Sin embargo, en el caso particular que muestras, dicha liberación jamás podría darse ya que pierdes toda referencia al objeto creado, que es lo que explica Al. Cuando asignas:

Código Delphi [-]
Image1.Picture.Graphic := TJpegImage.Create;

la parte derecha de la asignación crea un objeto que se pasa como parámetro al método TPicture.SetGraphic que citó Al. Pero este objeto no es el mismo que finalmente se guarda en el campo privado FGraphic de TPicture ya que Assign hará una copia del objeto. Como el parámetro se pierde después de la llamada, no tienes ninguna variable que apunte al objeto y por tanto no podrás hacer invocar al método Free.

Si realmente se piensa usar posteriormente el objeto creado, tendrías que usar asignar el resultado de Create a una variable y ésta asignarla a TPictureGraphic:

Código Delphi [-]
Graphic := TJpegImage.Create;
Image1.Picture.Graphic := Graphic;

Por otra parte, me da la impresión que nos hemos alejado de tu pregunta inicial. ¿Sigues teniendo el mismo problema que mencionas en el comentariio original?

// Saludos

ElMug 25-07-2012 09:58:07

Cita:

Empezado por roman (Mensaje 438152)
A lo que Al se refiere en el punto 1 es a que la mera asignación:

Código Delphi [-]Image1.Picture.Graphic:= TJpegImage.Create;


podría causar una excepción, por ejemplo, por falta de memoria o alguna otra razón imprevisible, como son las excepciones. Si ese fuera el caso, dado que tal excepción no está protegida por un bloque try-except, el código donde liberas a BS (creado anteriormente) no llegaría a ejecutarse y tendrías una fuga de memoria.

Por otra parte, estoy de acuerdo en que un objeto no necesariamente debe destruirse en el mismo contexto en donde se creó ya que posteriormente puede usarse. Sin embargo, en el caso particular que muestras, dicha liberación jamás podría darse ya que pierdes toda referencia al objeto creado, que es lo que explica Al. Cuando asignas:

Código Delphi [-]Image1.Picture.Graphic := TJpegImage.Create;


la parte derecha de la asignación crea un objeto que se pasa como parámetro al método TPicture.SetGraphic que citó Al. Pero este objeto no es el mismo que finalmente se guarda en el campo privado FGraphic de TPicture ya que Assign hará una copia del objeto. Como el parámetro se pierde después de la llamada, no tienes ninguna variable que apunte al objeto y por tanto no podrás hacer invocar al método Free.

Si realmente se piensa usar posteriormente el objeto creado, tendrías que usar asignar el resultado de Create a una variable y ésta asignarla a TPictureGraphic:

Código Delphi [-]Graphic := TJpegImage.Create; Image1.Picture.Graphic := Graphic;


Por otra parte, me da la impresión que nos hemos alejado de tu pregunta inicial. ¿Sigues teniendo el mismo problema que mencionas en el comentariio original?

// Saludos

Hola Roman,

Esta linea
Image1.Picture.Graphic:= TJpegImage.Create; NO causa excepcion (Lo verifique desde el inicio de mi desarrollo).

Ahora que si la computadora tenga falla de hardware, pues yo creo que primero fallarian otras cosas, no crees? Por que quererle echar ese chango en la espalda a este leve desarrollo que les deseo compartir?

Yo no veo nada practico en programar este procedimiento considerando que pueda fallar la memoria o el CPU, o alguna otra cosa de hardware.

Ademas, acuerdate que mi procedimiento se basa en que la imagen YA ESTA GUARDADA en el archivo, independientemente de mi proceso, lo cual demuestra que seria dificil que hubiera problema para manejar esa imagen.

Ahora, que si hay guardados en el Blob archivos defectuosos, o invalidos, eso pues no lo causa mi procedimiento, aunque SI los atrapa como error y los rechaza.

Ahora, si puede alguien mostrarme ejemplos, manuales, o documentacion donde alguien use el Try para CREATE, pues favor de hacerlo.

Yo si les puedo mostrar esta pagina de expertos donde NO usan Try en ningun CREATE, pues como les digo anteriormente, CREATE no levanta EXEPTion.

Y lo pueden ver aqui, donde el titulo del tema es :
How to correctly write Try..Finally..Except statements?

Bueno, hasta luego, y gracias por los comentarios.
.......................

P.S. El problema del mensaje original, es que no me entendieron la pregunta. No es un problema que "tenga". Ya recibi aclaracion en el foro de Lazarus. Pero parece ser que no hay respuesta definitiva.

Casimiro Notevi 25-07-2012 10:09:13

Cita:

Empezado por ElMug (Mensaje 438158)
Yo si les puedo mostrar esta pagina de expertos...

Eso me ha hecho gracia :D

ElMug 25-07-2012 10:26:45

Cita:

Empezado por Casimiro Notevi (Mensaje 438160)
Eso me ha hecho gracia :D

Quien quiere ver circo en todo, pues muy su parecer.

Casimiro Notevi 25-07-2012 11:23:44

¡Qué atrevida es la ignorancia! :confused:

roman 25-07-2012 17:45:06

Cita:

Empezado por ElMug (Mensaje 438158)

P.S. El problema del mensaje original, es que no me entendieron la pregunta. No es un problema que "tenga". Ya recibi aclaracion en el foro de Lazarus. Pero parece ser que no hay respuesta definitiva.

Yo sí entendí la pregunta: quieres saber si el formato con que se guarda una imagen en un campo blob es independiente de la aplicación con que se guarde o lea. También sé la respuesta: sí es independiente (creo que Delphius también lo mencionó). Si no puedes leer en una aplicación la imagen guardada en otra aplicación entonces hay un problema en la forma de leer la imagen.

// Saludos

ElMug 26-07-2012 03:52:35

Cita:

Empezado por roman (Mensaje 438179)
Yo sí entendí la pregunta: quieres saber si el formato con que se guarda una imagen en un campo blob es independiente de la aplicación con que se guarde o lea. También sé la respuesta: sí es independiente (creo que Delphius también lo mencionó). Si no puedes leer en una aplicación la imagen guardada en otra aplicación entonces hay un problema en la forma de leer la imagen.

// Saludos

Hola Roman,

Es un poco mas alla el asunto de "independiente de la aplicacion".

Creo que lo diria asi "independiente de los diversos generadores de aplicaciones", mi cuestion, y aun me interesa saber de otras personas al respecto, porque el asunto se aboca a la centralizacion de bases de datos, en las que diversas aplicaciones clientes accedan al dato Blob.

Si digo "un poco mas alla", es porque aunque se llene el viejo criterio de C. F. Codd en el significado de "la data debe de ser independiente de la aplicacion", pues si lo llena en si, cada plataforma generadora de aplicaciones. Eso ya es rutina en bases de datos actuales.

Pero, por ejemplo, si centralizamos una base de datos y generamos, por decir un .exe de acceso con Lazarus, y uno con Delphi, usando practicamente el mismo codigo, DEBERIA de leer las imagenes que guarde uno, el otro, y viceversa. Pero no estoy seguro que eso pase con los campos Blob, asi como se puden leer los textos y numericos con plena compatibilidad.

Se me ha informado que Lazarus (indebidamente segun mi punto de vista) le AGREGA unos bytes al archivo Blob, lo cual haria la data dependiente de la aplicacion que genere el generador de aplicaciones Lazarus. Y tambien se me ha informado que Delphi no le agrega nada.

Pero es un poco mas complicado de concluir, porque en Lazarus, puede uno utilizar otros componentes que no sean los "default" de Lazarus.

Mi plan es, ya indagando un poco mas, usar los componentes que mas sea compatibles en eso con los demas generadores de aplicaciones.

Averiguar todo esto, debido a que hay bastantes plataformas generadoras de aplicaciones, que usan diversas tecnologias como Java, C++, etc., no es tan facil para uno encarar todo eso, pues tendria uno que instalar varias plataformas y generar sus aplicaciones para ello.

Pero, si el detalle es solo con Lazarus, y todos las demas plataformas guardan el Blob igual, tal como lo extraen del archivo de la grafica, pues es mas simple de resolver.

Asi que el problema no es de "leer" sino de como se "guarda" o almacena el archivo segun las diversas plataformas de programacion, o los componentes que se usen.

Y si este tema ya se vio antes, y hay respuestas y conclusiones, y asesoria al respecto, pues mucho les agradeceria saber de ello, pues de mi parte no encuentro aun nada conclusivo.

Lo que si, es que ya verifique que hay deferencia entre como guarda Lazarus y como guarda el SQLite Administrator.

Aclaro tambien que esto no debe de tener nada que ver con cual database se use.

Tambien aclaro que me inclino a pensar que tal vez el problema sea solo con los componentes default de Lazarus, pero no puedo concluir eso aun, en definitiva.

Tampoco descarto que haya algo que no he considerado.

Gracias por el interes en ayudar a resolver esto.

roman 26-07-2012 06:22:14

Cita:

Empezado por ElMug (Mensaje 438229)
Lo que si, es que ya verifique que hay deferencia entre como guarda Lazarus y como guarda el SQLite Administrator.

Aclaro tambien que esto no debe de tener nada que ver con cual database se use.

Pues sí; estoy de acuerdo. No uso Lazarus de manera que no he probado, pero si es como dices, que agrega unos bytes extra pues sería, a mi parecer también, un comportamiento incorrecto. Lo que se guarde en el blob debe ser tal como si se guardara en un archivo en disco.

Si consigo algo de tiempo, haré unas pruebas con Lazarus y SQLite.

// Saludos

ElMug 26-07-2012 07:03:22

Gracias, Roman.

roman 02-08-2012 23:30:16

Bueno, hasta ahora es que he podido hacer algunas pruebas. A reserva de confirmalo más tarde, lo que puedo ver hasta el momento es que el problema no está en SQLite, sino en Lázarus.

Si guardo una imagen BMP en una tabla de SQLite usando el SQL Administrator, no puedo leerla con una aplicación Lázarus con el código esbozado anteriormente.

Sin embargo, si esa misma imagen la leo con una aplicación Delphi (usando ZEOS), la leo sin problema, tal como debe ser.

De aquí concluyo que el problema está en Lázarus. De hecho, si guardo la imagen que leo desde Lázarus en un archivo y examino el archivo con un editor binario, veo que su contenido no tiene nada que ver con el de la imagen original.

Habrá que estudiar si algo estamos haciendo mal con Lázarus o si es simple y llanamente un bug.

Seguiré probando...

// Saludos

Casimiro Notevi 02-08-2012 23:42:17

Por curiosidad, ¿cómo la guardas desde lazarus?

roman 03-08-2012 01:49:07

Esa parte no la he visto; únicamente he tratado de leer desde lázarus una imagen guardada con SQLite Administrator.

// Saludos

roman 03-08-2012 01:58:14

qP:-) ¡Ah! ¿Te refieres a cómo la guardo en un archivo? Pues desde el Stream con el método SaveToFile.

// Saludos

ElMug 03-08-2012 02:10:47

Cita:

Empezado por roman (Mensaje 438757)
Bueno, hasta ahora es que he podido hacer algunas pruebas. A reserva de confirmalo más tarde, lo que puedo ver hasta el momento es que el problema no está en SQLite, sino en Lázarus.

Si guardo una imagen BMP en una tabla de SQLite usando el SQL Administrator, no puedo leerla con una aplicación Lázarus con el código esbozado anteriormente.

Sin embargo, si esa misma imagen la leo con una aplicación Delphi (usando ZEOS), la leo sin problema, tal como debe ser.

De aquí concluyo que el problema está en Lázarus. De hecho, si guardo la imagen que leo desde Lázarus en un archivo y examino el archivo con un editor binario, veo que su contenido no tiene nada que ver con el de la imagen original.

Habrá que estudiar si algo estamos haciendo mal con Lázarus o si es simple y llanamente un bug.

Seguiré probando...

// Saludos

Muchas gracias, Roman.

Tengo esto que platicar al respecto:

Concuerdo contigo y posible cambie de componentes, o vea forma de modificar para que no agregue headers a la imagen. Sin embargo, tal vez requiera tambien cambiar la rutina que las lee.

Pero esta novedad me pone algunas dudas:
--------------------------------------------------
Hice la inversa, o sea de mandar a un archivo xxx.jpg o yyy.bmp con aplicacion Lazaro, y la guarda. Entonces, hice click en ella y el Windows image-viewer la abrio perfectamente, a pesar de que mande el blob identico como estaba en la base de datos (sqlite3).

Lo mismo con imagen .bmp.

Luego cree igualmente un archivo sin extension, solo xxx o yyy, y lo creo. Lo explore con Windows 7, y "no sabe" que tipo es (se hace el inocente). Entonce force al image viewer y si, las abre aun SIN extension.

Luego hago click en las imagenes sin extension, y me pide con QUE abrirlas, le obligue a Opera (browser) y las abre inmediatamente y correctamente sin chistar.

El archivo lo cree, segun yo, sin que se le quiten headers, mas si es asi, tendria que ver con CreateBlobStream, la cual yo considero que es generica para todo tipo de Blob.

Si acaso CreateBlobStream checa y quita headers, entonces Lazarus se la ha complicado demasiado, diria.

Lo bueno es que en Lazarus se puede meter uno al source-code y examinarlo/cambiarlo, pero eso seria algo delicado, y aun no lo contemplo.

No me es claro si una imagen grabada con Lazarus la lees con Delphi?

De inicio, no quise usar Zeus porque lei que ya esta sin desarrollos, pero lo voy a tratar. Que bien que me indicas al respecto.

Me les reporto si hay novedad, y te repito que te agradezco mucho la ayuda. Me es muy util saber que Delphi lee correctamente a SQLite Adminstrator.

Saludos a todos.

roman 03-08-2012 22:10:30

A ver, creo que ya está un poco más claro, aunque sigue bastante oscuro :D.

Parece ser que siempre sí, la culpa es de SQLite, pero componentes como ZEOS saben cómo arreglar las cosas mientras que los componentes nativos de Lázarus no, o al menos todavía no encuentro como.

El problema con SQLite (y es que apenas hago mis pininos con este gestor) no es tanto que añada encabezados a un blob o que lo guarde en una forma no estándar. El problema es que SQLite usa los caracteres #0 (NULL) como separador de columnas de manera que no puede guardar bytes cero en un campo BLOB. Tal parece entonces que sustituye los ceros por un caracter de porcentaje %. Se supone que esto ya no es así en la versión 3 pero es con la que he estado haciendo pruebas y así es como guarda las imágenes.

No he instalado los componentes ZEOS en Lázarus (no pude :o) pero examinando el código fuente, me encontré esto:

Código Delphi [-]
function DecodeString(Value: string): string;
var
  SrcLength, DestLength: Integer;
  SrcBuffer, DestBuffer: PChar;
begin
  SrcLength := Length(Value);
  SrcBuffer := PChar(Value);
  SetLength(Result, SrcLength);
  DestLength := 0;
  DestBuffer := PChar(Result);

  while SrcLength > 0 do
  begin
    if SrcBuffer^ = '%' then
    begin
      Inc(SrcBuffer);
      if SrcBuffer^ <> '0' then
        DestBuffer^ := SrcBuffer^
      else DestBuffer^ := #0;
      Inc(SrcBuffer);
      Dec(SrcLength, 2);
    end
    else
    begin
      DestBuffer^ := SrcBuffer^;
      Inc(SrcBuffer);
      Dec(SrcLength);
    end;
    Inc(DestBuffer);
    Inc(DestLength);
  end;
  SetLength(Result, DestLength);
end;

Esta función la usa en el método GetBlob de la clase TZSQLiteResultSet:

Código Delphi [-]
function TZSQLiteResultSet.GetBlob(ColumnIndex: Integer): IZBlob;
var
  Stream: TStream;
begin
{$IFNDEF DISABLE_CHECKING}
  CheckBlobColumn(ColumnIndex);
{$ENDIF}
  Stream := nil;
  try
    if not IsNull(ColumnIndex) then
    begin
      if TZAbstractResultSetMetadata(Metadata).GetColumnType(ColumnIndex)
        <> stBinaryStream then
        Stream := TStringStream.Create(GetString(ColumnIndex))
      else Stream := TStringStream.Create(DecodeString(GetString(ColumnIndex)));
      Result := TZAbstractBlob.CreateWithStream(Stream)
    end else
      Result := TZAbstractBlob.CreateWithStream(nil);
  finally
    if Assigned(Stream) then
      Stream.Free;
  end;
end;

Con un aplicación delphi abrí el archivo guardado desde el BlobField, le pase la función anterior y vacié la cadena nuevamente en un archivo y voilà, la imagen ya era la correcta.

La función compila sin problemas en Lázarus, pero no he podido usarla porque la clase TStringStream no es compatible con la de delphi así que todavía no encuentro cómo usarla para almacenar el blob, convertirlo y vaciarlo en un TBitmap.

Seguiremos informando...

// Saludos

Casimiro Notevi 03-08-2012 22:25:09

Qué extraño resulta ese asunto :confused:


La franja horaria es GMT +2. Ahora son las 06:38:55.

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