Foros Club Delphi

Foros Club Delphi (http://www.clubdelphi.com/foros/index.php)
-   Firebird e Interbase (http://www.clubdelphi.com/foros/forumdisplay.php?f=19)
-   -   Cargar imagen desde Firebird (http://www.clubdelphi.com/foros/showthread.php?t=92375)

Angel.Matilla 11-10-2017 18:25:26

Cargar imagen desde Firebird
 
Estoy usando este código que me facilitaron en otro foro:
Código:

TJPEGImage*  __fastcall BlobToJpg(TBlobField *CampoBLOB)
{
    if(!CampoBLOB->Value.IsEmpty())
    {
        TJPEGImage *JPeg = new TJPEGImage;
        TMemoryStream *StreamTmp = new TMemoryStream;
        CampoBLOB->SaveToStream(StreamTmp); //sacamos el jpg y se convierte a stream
        StreamTmp->Seek(0,soFromBeginning);
        JPeg->LoadFromStream(StreamTmp);
        return(JPeg);
    }
    else
        return(NULL);
}

para poder sacar imágenes desde una tabla Firebird. Siguiendo las instrucciones del autor trato de cargar la misma en un objeto TImage de la siguiente forma a partir de un query:
Código:

Imagen->Picture->Assign(::BLOBtoJPG(Query->FieldByName("Imagen")));
pero tanto con esta sintaxis como de esta otra forma:
Código:

Imagen->Picture->Assign(::BLOBtoJPG(Query->FieldByName("Imagen")->Value));
En ambos casos me da error al compilar; en el primer caso me dice:
Cita:

E2034 Cannot convert 'TField *' to 'TBlobField *'
y en el segundo algo parecido:
Cita:

E2034 Cannot convert 'Variant' to 'TBlobField *'
¿Alguna idea para resolver el problema?

TOPX 11-10-2017 18:48:25

Si fuera yo, definiría el tipo del parámetro CampoBLOB como TField, de manera que no tenga que preocuparme por el code smell del casteo.
-

Angel.Matilla 11-10-2017 19:14:53

Haré la prueba.

Angel.Matilla 11-10-2017 19:17:58

Cita:

Empezado por TOPX (Mensaje 521694)
Si fuera yo, definiría el tipo del parámetro CampoBLOB como TField, de manera que no tenga que preocuparme por el code smell del casteo.

No funciona. Y al compliar me he dado cuenta de por qué: Fíjate que en la función hay un SaveToStream que si se usa en la definición un TField en lugar de un TBlobField da error.
Cita:

E2316 'SaveToStream' is not a member of 'TField'

Angel.Matilla 11-10-2017 19:24:58

Dicho de otra forma: ¿Qué habría que poner detrás del FieldByName? Igual que ponemos AsString, AsInteger o AsDateTime, ¿qué se pone en caso de campos BLOB? Porque AsBlob a mi me da error en BCB 6.

TOPX 11-10-2017 19:38:02

Cita:

Empezado por Angel.Matilla (Mensaje 521696)
No funciona. Y al compliar me he dado cuenta de por qué: Fíjate que en la función hay un SaveToStream que si se usa en la definición un TField en lugar de un TBlobField da error.

Ok, entonces hay que hacer el casteo al tipo de dato del parámetro, osea algo como
Código:

::BLOBtoJPG((TBlobField*)Query->FieldByName("Imagen"))
Cita:

Empezado por Angel.Matilla (Mensaje 521697)
Igual que ponemos AsString, AsInteger o AsDateTime, ¿qué se pone en caso de campos BLOB? Porque AsBlob a mi me da error en BCB 6.

Eso es equivocado; el método BLOBtoJPG no está pidiendo un valor, está pidiendo un objeto.
-

ecfisa 12-10-2017 00:14:59

Hola.

Solo como observación, me parece ver un problema de memory leak en esa función...

Saludos :)

ecfisa 12-10-2017 02:03:29

Hola de nuevo.

De este modo me funciona bien:
Código PHP:

...
#include <jpeg.hpp>

void blobTojpgTField *fldTImage *img )
{
  
TBlobField *static_cast <TBlobField*> ( fld );

  if( !
B->Value.IsEmpty() ) {
    
TJPEGImage *= new TJPEGImage;
    try {
      
J->Assign);
      
img->Picture->Bitmap->Height J->Height;
      
img->Picture->Bitmap->Width  J->Width;
      
img->Picture->Bitmap->Canvas->Draw00);
    }
    
__finally {
      
delete J;
    }
  }



Y si gustas usar streams,
Código PHP:

void blobTojpg2TField *fldTImage *img )
{
  
TBlobField *static_cast <TBlobField*> ( fld );

  if ( !
B->Value.IsEmpty() ) {
    
TStream *Stream = new TMemoryStream;
    
TJPEGImage *Jpg = new TJPEGImage;
    try {
     
B->SaveToStreamStream );
     
Stream->Seek0soFromBeginning);
     
Jpg->LoadFromStreamStream );
     
img->Picture->AssignJpg );
    }
    
__finally {
      
delete Stream;
      
delete Jpg;
    }
  }


Llamada (para ambos casos):
Código PHP:

void __fastcall TForm1::DataSetAfterScrollTDataSet *DataSet )
{
   
blobTojpgIBDataSet1->FieldByName("IMAGE"), Image1 ); // o blobToJpg2( ...


El resultado en ambos casos:


Saludos :)

Angel.Matilla 13-10-2017 11:02:29

Cita:

Empezado por TOPX (Mensaje 521700)
Ok, entonces hay que hacer el casteo al tipo de dato del parámetro, osea algo como
Código:

::BLOBtoJPG((TBlobField*)Query->FieldByName("Imagen"))

¿Ves? Estas cosas no se me ocurren nunca :(. Ha funcionado a la perfección.
Cita:

Empezado por TOPX (Mensaje 521700)
Eso es equivocado; el método BLOBtoJPG no está pidiendo un valor, está pidiendo un objeto.

Ya, pero si se puede usar como parámetro (Query->ParamByName("Imagen")->AsBlob), alguna forma tiene que haber para llamar a ese objeto desde un FieldByName.
Cita:

Empezado por ecfisa (Mensaje 521702)
Solo como observación, me parece ver un problema de memory leak en esa función...

¿Dónde? A mi no me da ningún error de memoria.

ecfisa 13-10-2017 16:58:52

Cita:

Empezado por Angel.Matilla (Mensaje 521724)
¿Dónde? A mi no me da ningún error de memoria.

Código:

TJPEGImage*  __fastcall BlobToJpg(TBlobField *CampoBLOB)
{
    if(!CampoBLOB->Value.IsEmpty())
    {
        TJPEGImage *JPeg = new TJPEGImage;            // <==== (1)
        TMemoryStream *StreamTmp = new TMemoryStream;  // <==== (2)
        CampoBLOB->SaveToStream(StreamTmp); //sacamos el jpg y se convierte a stream
        StreamTmp->Seek(0,soFromBeginning);
        JPeg->LoadFromStream(StreamTmp);
        return(JPeg);
    }
    else
        return(NULL);
}

Tal vez no todavia...

Cuando solicitas recursos usando VCL es tu responsabilidad liberarlos, Builder C++ no liberará automáticamente la memoria ocupada, aún cuando se trate de variables locales a la función.

En ese código no se libera la variable SteramTmp; y lo mas serio es que no se puede liberar la variable JPeg sin dejar inoperante la función.

Saludos :)

TOPX 14-10-2017 00:30:33

Cita:

Empezado por ecfisa (Mensaje 521731)
Cuando solicitas recursos usando VCL es tu responsabilidad liberarlos, Builder C++ no liberará automáticamente la memoria ocupada, aún cuando se trate de variables locales a la función.

Es muy importante esto que dice ecfisa, hay que aplicar esa directriz siempre.

Verguenza para mí que no lo había pillado en el código inicial.
-

Angel.Matilla 14-10-2017 10:56:53

¡Pues no me había dado cuenta! Tenéis razón. El problema es cómo liberar ambas variables ya que el jpeg es lo que devuelve como respuesta la función; no se me ocurre como. Acaso sea mejor sistema el que me sugieres en tu código, invocando en la función tanto el campo blob como el TImage en que ha de situarse; de esa forma se pueden liberar recursos. Voy a probarlo.


La franja horaria es GMT +2. Ahora son las 15:15:31.

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