Page 1 of 1

grabar y llamar un archivo de imagen en una tabla mySQL

PostPosted: Thu Apr 22, 2010 5:26 am
by armando.lagunas
Hola Amigos:

Quisiera saber si alguien me puede ayudar a realizar esta acción.

lo que quiero conseguir es que un archivo de imagen, (jpg o bmp), que fue sacada con una cámara digital, la pueda grabar en un campo de una tabla Mysql, y posteriormente mostrarla con mi aplicación en una pantalla de ingreso.

espero que me hayan entendido.

saludos desde Chile

Re: grabar y llamar un archivo de imagen en una tabla mySQL

PostPosted: Thu Apr 22, 2010 12:00 pm
by antolin
Armando, yo he conseguido hacerlo así, pero solo para bitmaps tipo BMP de 24 bits (lo he probado con un bitmap pequeño de 256X256, con más grandes supong que fucionará igual):

Necesitas tres campos en tu base d datos: ANCHO, ALTO y DATOS (de la imagen). los dos primeros numéricos y el último un MEMO.

Code: Select all  Expand view
FUNCTION Prueba()
   LOCAL oDlg
   LOCAL nWidth,nHeight
   LOCAL hBmp
   LOCAL cImg  := CurDrive()+":\"+CurDir()+"\PAISAJE.bmp"  // imagen BMP de 24 bits
   *
   cImg := MEMOREAD( cImg )
   nWidth  = ASC(SubStr(cimg,19,1))+(ASC(SubStr(cimg,20,1))*256)+(ASC(SubStr(cimg,21,1))*65536)
   nHeight = ASC(SubStr(cimg,23,1))+(ASC(SubStr(cimg,24,1))*256)+(ASC(SubStr(cimg,25,1))*65536)
   cImg := SubStr(cImg,55)  // DATOS DE LA IMAGEN
   *
   hBmp := BuildImage( nWidth, nHeight, cImg )
   *
   DEFINE DIALOG oDlg  SIZE 350,350
      oDlg:bPainted := { |hDc| PaintPrb(hDc,hBmp) }
   ACTIVATE DIALOG oDlg CENTER
 
   DeleteObject( hBmp )

RETURN NIL
*
FUNCTION PaintPrb(hDc,hBit)
    PalBmpDraw( hDc,10,10,hBit)
RETURN NIL


Me explico: Carga la imagen como si fuera un TXT, con MEMOREAD(), calculas nWidth, nHeight y cImage como en el ejemplo y guarda nWidth en el campo ANCHO, nHeight en el campo ALTO y cImage en el MEMO DATOS.

Para reconstruir la imagen he creado la función en C++ (borland 5.5) BUILDIMAGE, este es el código.

Code: Select all  Expand view
HB_FUNC( BUILDIMAGE )   // BuildImage( nWidth, nHeight, cImage )
  {
   HDC  hDc1 = CreateCompatibleDC( NULL ) ;

   int nWidth  = hb_parni( 1 ) ;
   int nHeight = hb_parni( 2 ) ;

   LPSTR cImage = hb_parc( 3 ) ;

   BITMAPINFO bmi     ;
   HBITMAP    hBitmap ;
   VOID      *pvBits  ;

   int i,j,k,v ;

   int nQWidth = ((( nWidth * 24 + 31 ) & ~31 ) >> 3 ) ;

   bmi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER ) ;
   bmi.bmiHeader.biWidth       = nWidth          ;
   bmi.bmiHeader.biHeight      = nHeight         ;
   bmi.bmiHeader.biPlanes      = 1               ;
   bmi.bmiHeader.biBitCount    = 24               ;
   bmi.bmiHeader.biCompression = BI_RGB            ;
   bmi.bmiHeader.biSizeImage   = nQWidth * nHeight  ;

   hBitmap = CreateDIBSection( hDc1, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0 ) ;

   v = 0 ;
   for ( i = 0; i < nHeight; ++i )
       {
       k = i * nQWidth ;
       for ( j = 0; j < (nWidth*3); j += 3 )
           {
           ( (BYTE *) pvBits )[k+j+0] = ( BYTE ) cImage[ v+0 ] ;
           ( (BYTE *) pvBits )[k+j+1] = ( BYTE ) cImage[ v+1 ] ;
           ( (BYTE *) pvBits )[k+j+2] = ( BYTE ) cImage[ v+2 ] ;
           v += 3 ;
           }
       }

   DeleteDC( hDc1 ) ;

   hb_retnl( ( LONG ) hBitmap ) ;
   }
 


A mi me funciona perfectamente.

Para compilar la función en C++ tendrás que utilizar las cabeceras pertinentes (alguien habrá que sepas cuales son). Lo que pasa es que yo lo he hecho incluyendo esa función en una librería en la que tengo varias funciones de C++ y no se que cabeceras se utilizan para FWH con PRAGMA (nunca lo he heco).

Re: grabar y llamar un archivo de imagen en una tabla mySQL

PostPosted: Thu Apr 22, 2010 12:15 pm
by antolin
Armando, creo que he cometido un error, esto solo funciona cuando el numero de bits por linea de la imagen es multiplo de 4. Hoy no tengo tiempo de seguir experimentando pero lo miro y te lo posteo.

Re: grabar y llamar un archivo de imagen en una tabla mySQL

PostPosted: Thu Apr 22, 2010 12:28 pm
by antolin
Este si funciona:

Code: Select all  Expand view
HB_FUNC( BUILDIMAGE )   // BuildImage( nWidth, nHeight, cImage )
  {
   HDC  hDc1 = CreateCompatibleDC( NULL ) ;

   int nWidth  = hb_parni( 1 ) ;
   int nHeight = hb_parni( 2 ) ;

   LPSTR cImage = hb_parc( 3 ) ;

   BITMAPINFO bmi     ;
   HBITMAP    hBitmap ;
   VOID      *pvBits  ;

   int i,j,k,v ;

   int nQWidth = ((( nWidth * 24 + 31 ) & ~31 ) >> 3 ) ;

   bmi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER ) ;
   bmi.bmiHeader.biWidth       = nWidth          ;
   bmi.bmiHeader.biHeight      = nHeight         ;
   bmi.bmiHeader.biPlanes      = 1           ;
   bmi.bmiHeader.biBitCount    = 24          ;
   bmi.bmiHeader.biCompression = BI_RGB          ;
   bmi.bmiHeader.biSizeImage   = nQWidth * nHeight   ;

   hBitmap = CreateDIBSection( hDc1, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0 ) ;

   v = 0 ;
   for ( i = 0; i < nHeight; ++i )
       {
       k = i * nQWidth ;
       for ( j = 0; j < (nWidth*3); j += 3 )
           {
           ( (BYTE *) pvBits )[k+j+0] = ( BYTE ) cImage[ v+0 ] ;
           ( (BYTE *) pvBits )[k+j+1] = ( BYTE ) cImage[ v+1 ] ;
           ( (BYTE *) pvBits )[k+j+2] = ( BYTE ) cImage[ v+2 ] ;
           v += 3 ;
           }
          while ( v%4 > 0 )
                  {
                   ++v ;
                  }
       }

   DeleteDC( hDc1 ) ;

   hb_retnl( ( LONG ) hBitmap ) ;
   }
 

Re: grabar y llamar un archivo de imagen en una tabla mySQL

PostPosted: Fri Apr 23, 2010 1:46 am
by jose_murugosa
Este es un tema que me resulta muy interesante, por favor, si logran que funcione plenamente y determinar cuales archivos de cabezal se deben incluir, háganlo saber :roll: , ya que me parece una función muy útil para una aplicación que me encuentro desarrollando :wink: .

Gracias desde ya a los compañeros del foro, verdaderamente es un placer contar con Uds. :D

Re: grabar y llamar un archivo de imagen en una tabla mySQL

PostPosted: Fri Apr 23, 2010 3:19 am
by armando.lagunas
gracias por contestar, voy a realizar las pruebas y les cuento como me fue.

gracias.

Re: grabar y llamar un archivo de imagen en una tabla mySQL

PostPosted: Fri Apr 23, 2010 3:40 am
by sjingo
Armando

Si usas ADO, lo puedes hacer mediante el objeto ADO Stream. Para ello (como yo lo hago) previamente debes crear un objeto Connection, un objeto Recordset que venga vacío para agregarle el nuevo registro con la foto y un Objeto ADO Stream. El objeto Stream carga el archivo que posteriormente será enviado al objeto recordset. El regreso se hace a la inversa. Se carga la foto al objeto stream desde el recordset para posteriormente grabarlo en una carpeta local, y ya copiado lo podemos usar como queramos. A continucación parte del código, Ojo que el objeto connection ya está creado con oCon:TOleAuto():New("adodb.connection")


Code: Select all  Expand view
//Recordset para la foto grande
TRY
    oRsFoto := TOleAuto():New("adodb.recordset")
CATCH oError
    MsgStop( "No se pudo crear el recordset para la Foto !", CAR_VERSION)
    RETURN .f.
END

//Configuramos el recordset
oRsFoto:CursorLocation := adUseClient
oRsFoto:LockType := adLockOptimistic
oRsFoto:CursorType := adOpenKeyset//adOpenDynamic
//lo siguiente recupera una tabla vacia
oRsFoto:Source := "SELECT cod,cod_veh,foto_p,foto,tipo,fecha,nota FROM fotos_veh where cod = 0 "
oRsFoto:ActiveConnection(oCon)

TRY
    oRsFoto:Open()
CATCH oError
    MsgStop( "No se pudo abrir la Tabla de la Foto!", car_version)
    ShowError(oError,oCon)
    RETURN .f.
END

//----Objeto stream para la foto grande
TRY
    ::oStream := TOleAuto():New("adodb.Stream")
CATCH oError
    MsgStop( "No se pudo crear el Stream para leer la Foto !", CAR_VERSION)
END
// Especifica el tipo de datos ( binario )  
::oStream:Type := adTypeBinary

TRY
    ::oStream:Open()
CATCH oError
    MsgStop( "No se pudo establecer la conexion stream!", car_version)
    ShowError(oError,oCon)
    return .f.
END
::oStream:LoadFromFile(cfile)// cfile puede ser obtenida con cgetfile()  

oRsfoto:Addnew()
oRsfoto:fields('cod_veh'):Value := ::oRsV:Fields('cod_veh'):Value
try
    oRsFoto:Fields('foto'):Value = ::oStream:Read()
catch oError
    MsgStop("Problemas al subir la fotografía")
    ShowError(oError,oCon)
end
oRsFoto:Fields('fecha'):Value = fecha
oRsFoto:Fields('nota'):Value = nota
oRsFoto:Update()
::oStream:Close()

Ahora para leerla
Code: Select all  Expand view

//Recordset para la foto grande
TRY
    oRsFoto := TOleAuto():New("adodb.recordset")
CATCH oError
    MsgStop( "No se pudo crear el recordset para la Foto !", CAR_VERSION)
    RETURN .f.
END

//Configuramos el recordset que me dovolvera una foto
oRsFoto:CursorLocation := adUseClient
oRsFoto:LockType := adLockOptimistic
oRsFoto:CursorType := adOpenKeyset//adOpenDynamic
oRsFoto:Source := "SELECT cod,foto,tipo FROM fotos_veh where cod ="+ nCod
oRsFoto:ActiveConnection(oCon)

TRY
    oRsFoto:Open()
CATCH oError
    MsgStop( "No se pudo abrir la Tabla de la Foto!", car_version)
    ShowError(oError,oCon)
    RETURN .f.
END

TRY
    ::oStream := TOleAuto():New("adodb.Stream")
CATCH oError
    MsgStop( "No se pudo crear el Stream para leer la Foto !", CAR_VERSION)
END

// Especifica el tipo de datos ( binario )  
::oStream:Type := adTypeBinary

TRY
    ::oStream:Open()
CATCH oError
    MsgStop( "No se pudo establecer la conexion stream!", car_version)
    ShowError(oError,oCon)
    return .f.
END

if !lisDir("imgtmp")
    lMkDir("imgtmp")
endif
if !lisDir("imgtmp\" + cFold )
     lMkDir("
imgtmp\" + cFold )
endif

if !oRsFoto:EOF .and. !oRsFoto:BOF
    //Graba los datos en el objeto stream  
    IF oRsFoto:Fields('foto'):Value !=nil
        ::oStream:Write(oRsFoto:Fields('foto'):Value)
        cfoto:="
imgtmp\" + cFold+"fg"+alltrim(str(oRsFoto:Fields('cod'):Value)) + "." + alltrim(oRsFoto:Fields('tipo'):Value)
        if file(cfoto)
            ferase(cfoto)
        endif
        //Se graba un  archivo temporal  en disco
        ::oStream:SaveToFile(cfoto, adSaveCreateOverWrite)  
        if fsize(cfoto)>0  
            oImg:LoadBmp(cfoto)
        else
            oImg:LoadImage( "
shade" )
        endif
        oimg:Refresh()
    endif
endif

::oStream:Close()
oRsfoto:Close()

Una vez bajado lo podemos usar a nuestro gusto, yo lo he cargado a un objeto Image.
Es una opción más que lo puedes probar.

Un saludo

Marcelo Jingo

Re: grabar y llamar un archivo de imagen en una tabla mySQL

PostPosted: Fri Apr 23, 2010 4:01 am
by armando.lagunas
estimado sjingo:

lamentablemente, no utilizo ADO, realizo mi conexión por ODBC 5.1 de mysql, pero me has dado una luz con el codigo que publicaste, asi que lo voy a estudiar a fondo para ver la posibilidad de transformarlo y hacerlo funcionar con ODBC.

cuando tenga algo concreto lo voy a publicar para que quede a disposición de la comunidad Fivewinera.

bueno, si alguien más me pueda ayudar, estaré atento a sus sugerencias.

saludos desde Chile.

Re: grabar y llamar un archivo de imagen en una tabla mySQL

PostPosted: Fri Apr 23, 2010 4:36 am
by Daniel Garcia-Gil
Saludos...

la clase TImagen cuenta con esa propiedad y existe la funcion FILoadFromMemory, esta ubicada en fwh\source\image.prg, usa freeimage.dll es tan simple como hacer

MiCampo = MemoRead( "mifoto.jpg" )

hBmp = FILoadFromMemory( MiCampo )

o usar la propia clase TImaga para que realice dicha labor

lamentablemente dentro de los ejemplo de fivewin no hay uno sobre dicha funcionalidad, pero no deberia ser muy complicado y aparenta ser muy facil usarlo

igualmente puden revisar este post del colega Cesar Lozada que patocino la idea inicial: http://forums.fivetechsupport.com/viewtopic.php?p=76883#p76883

Re: grabar y llamar un archivo de imagen en una tabla mySQL

PostPosted: Fri Apr 23, 2010 4:49 am
by Daniel Garcia-Gil
Saludos...

Ahora si solo usas .BMP creo que la funcion CreateMemBitmap te podria servir tambien, o usando la clase TBitmap el metodo LoadFromString revisa este otro post: http://forums.fivetechsupport.com/viewtopic.php?p=49250#p49250

Re: grabar y llamar un archivo de imagen en una tabla mySQL

PostPosted: Fri Apr 23, 2010 8:49 am
by antolin
Armando, ayer te contesté de forma apresurada, y creé la función a partir de otras mias, pero ahora he dado con la tecla para guardar cualquier tipo de BMP.

Carga la imagen con MEMOREAD como antes, pero no hay que descomponerla, guárdala tal cual en un campo MEMO, BUILDIMAGE() te devolverá el handle de la imagen cuando lo necesites.

Code: Select all  Expand view

LOCAL cImage
cImage := MemoRead( "C:\....\Imagen.Bmp")
BuilImage( cImage )
 

La nueva función sería esta:

Code: Select all  Expand view
HB_FUNC( BUILDIMAGE )   // BuildImage( cImage )
  {
   HDC  hDc1 = CreateCompatibleDC( NULL ) ;

   LPSTR cImage = hb_parc( 1 ) ;

   HBITMAP      hBitmap ;
   BITMAPINFO   bmi     ;
   BITMAPFILEHEADER bmh ;

   VOID       * pvBits ;
   BYTE       * pvDats ;
   BITMAPINFO * lpbi   ;

   pvDats  = &cImage[0] ;
   bmh     = * ( BITMAPFILEHEADER * ) pvDats ;
   pvDats += 14 ;
   lpbi    = ( BITMAPINFO * ) pvDats ;
   pvDats += ( bmh.bfOffBits-14 ) ;
   bmi     = * lpbi ;

   hBitmap = CreateDIBSection( hDc1, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0 ) ;

   SetDIBits( hDc1, hBitmap, 0L, bmi.bmiHeader.biHeight, pvDats, ( LPBITMAPINFO ) lpbi, ( DWORD ) DIB_RGB_COLORS ) ;

   DeleteDC( hDc1 ) ;

   hb_retnl( ( LONG ) hBitmap ) ;
   }

 


Y nada más. Así de simple. Como te decía, sirve para cualquier BMP.

Re: grabar y llamar un archivo de imagen en una tabla mySQL

PostPosted: Sat Apr 24, 2010 4:23 am
by armando.lagunas
gracias por contestar, estoy probando con sus aportaciones, apenas me resulte las voy a publicar.

hay una acotación, el tipo de campo que debo ocupar es tipo BLOB en una tabla MYSQL, y lo ideal es tener por separado, una tabla con los datos de la persona y en otra, la imagen con un campo indice apuntando a la principal.


saludos desde Chile.
:D