4.3. Tratamiento de ficheros

Vamos a contar como trabajar con documentos y ficheros de cualquier tipo. Añadimos ejemplos de como insertar un fichero en la BD, como mostrar una imagen que se encuentra en el servidor en un campo CWImagen, como insertar N tuplas en una BD a partir de un fichero XML.

4.3.1. Manejo de ficheros de imágenes

En este primer ejemplo vamos a mostrar como crear un mantenimiento que almacene imágenes en la BD (en nuestro ejemplo PostgreSQL) y las muestre en un CWImagen. Para conseguir esto necesitamos que en la interfaz (en la tpl) aparezcan dos plugins especiales de tratamiento de ficheros (CWUpLoad y el CWImagen).

El primero de ellos, el CWUpLoad, nos permite seleccionar un fichero ubicado en el ordenador cliente y subirlo al servidor Web. El contenido de este fichero se almacena en una ubicación temporal y el programador es el encargado de hacer uso de él. Posteriormente mostraremos los métodos que gvHidra proporciona para que se tenga acceso a la información de dicho fichero.

El segundo de ellos es el CWImagen, que permite mostrar una imagen en un panel a partir de una URL.

A continuación mostramos un ejemplo del panel resultado:

Para conseguir esto tenemos que tener en cuenta las dos partes que van a suponer trabajo para el programador, por un lado el tratamiento del fichero imagen cuando el usuario lo selecciona con el CWUpLoad y lo quiere almacenar en el servidor y, por otro, la representación de un fichero imagen en el CWImagen. El primero de estos pasos necesita de un acceso a la información del fichero y la inserción del mismo en la tupla que se esté modificando. A continuación mostramos el código que se ha insertado en el panel para resolverlo.

public function postModificar($objDatos) 
{
    do
    {
      $datosFile = $objDatos->getFileInfo('ficheroUpload');
      if(is_array($datosFile) and ($datosFile['tmp_name']!='')){
        $fichero = pg_escape_bytea(file_get_contents($datosFile['tmp_name']));
        $res = $this->operar("update tinv_imagenes SET fichero='{$fichero}', nombre='".$datosFile['name']."' where id=".$objDatos->getValue('id'));
        if($res==-1)
        {
          $this->showMensaje('APL-25');
          return -1;
        }
      }      
    } while($objDatos->nextTupla());  
    return 0;
  }

De este código destaca, en primer lugar, el método getFileInfo() del objeto objDatos. Este método devuelve el array de información del HTTP que indica los datos del fichero que ha subido el usuario (nombre, tipo, ubicación,...). A partir de esta información el programador ya puede hacer uso del fichero; y, en este caso, decide insertarlo en una BD PostgreSQL. Para ello va a hacer uso de un campo de tipo Bytea y, por esta razón, utiliza las siguientes premisas. Primero, tras obtener el contenido del fichero (función php file_get_contents) utiliza la función nativa de php pg_escape_bytea para poder construir la consulta. Construye la consulta incluyendo el contenido (escapado) del fichero entre llaves. De este modo la instrucción no tiene problemas al pasar el array de bytes al PosgreSQL. El otro punto complejo de esta pantalla está en la recuperación de la imagen de la BD. Para ello debemos obtener la imagen del campo bytea y almacenarla en una ubicación accesible por nuestro plugin CWImagen. Por esta razón, en este caso hemos decidido crear un directorio imagenes en la raiz de nuestro proyecto (también se podria usar el templates_c que ya usamos para smarty) donde se crearán los ficheros que se quieran visualizar. Luego se asignará al CWImagen la ruta de cada uno de ellos. El código que se encarga de realizar esto es:

public function postEditar($objDatos) 
{
    //Comprobamos si tiene imagen y en este caso la cargamos.
    $res = $objDatos->getAllTuplas();
    foreach($res as $indice => $tupla)
    {
      if($tupla['fichero']!='')
      {
        $nuevoFichero = "imagenes/".$tupla['nombre'];
        if(!file_exists($nuevoFichero))
        {
          $imagen = pg_unescape_bytea($tupla['fichero']);
          $gestor = fopen($nuevoFichero, 'x');
          fwrite($gestor,$imagen);
        }
        $res[$indice]['fichero'] = $nuevoFichero;
      }
    }
    $objDatos->setAllTuplas($res);
}//Function postEditar

Como se puede suponer del código anterior, el CWImagen se llama fichero.

{CWImagen nombre="fichero"}
{CWUpLoad nombre="ficheroUpload" size="10" textoAsociado="Si quieres cambiar la imagen..."}

4.3.2. Manejo de ficheros de cualquier tipo

Puede darse el caso que queramos almacenar documentos de cualquier tipo en la BD. Para ello tendríamos que hacer una simple modificación al código anterior. Es evidente que ya no nocesitariamos el plugin CWImagen. Lo único que necesitamos es un botón que lance una acción particular que recupera el documento de la BD y lance el mismo a una nueva ventana del navegador. El código que se encarga de realizar esto es el siguiente

public function accionesParticulares($str_accion, $objDatos) 
{
    switch ($str_accion) 
    {
      case 'verFichero':
        //Bucle para crear un listado para cada peticion seleccionada:
        $objDatos->setOperacion("seleccionar");
        $m_datosSeleccionados = $objDatos->getAllTuplas();
        $res = $this->consultar("SELECT fichero,tipo from tinv_documentos WHERE id = ".$m_datosSeleccionados[0]['id']);
        if($res[0]['fichero']!='')
        {
          header('Content-Type: '.$res[0]['tipo']);
          header('Content-Disposition: attachment; filename="'.$m_datosSeleccionados[0]['nombre'].'"');
          print(pg_unescape_bytea($res[0]['fichero']));
          ob_end_flush ();
          //Para que no continue la ejecución de la página
          die;        
        }   
        $actionForward = $objDatos->getForward('correcto');
       break;
    }
    return $actionForward;          
}  

Como nota podemos indicar que, a diferencia del ejemplo anterior, en este ejemplo, al almacenar el documento en la BD se almacena no sólo su contenido, sino también el tipo. De este modo se puede asignar al header para que el navegador lo abra con el programa adecuado para tratarlo dependiendo de la extensión (MIME-TYPES).

4.3.3. Importar datos a la BD desde fichero

Este es otro de los ejemplos típicos con los que nos podemos encontrar. Imaginemos el caso de una aplicación que carga sus datos a partir de una fuente externa como, por ejemplo, un XML. Para este ejemplo sólo utilizaremos el plugin CWUpLoad y el código sería el siguiente:

public function accionesParticulares($str_accion, $objDatos) 
{
    switch ($str_accion) 
    {
      case 'importarFichero':
        $objDatos->setOperacion('external');
        $datosFile = $objDatos->getFileInfo('ficheroUpload');
        $contenidoFichero = utf8_encode(file_get_contents($datosFile['tmp_name']));
        $dom = domxml_open_mem($contenidoFichero,
          DOMXML_LOAD_PARSING + //0
          DOMXML_LOAD_COMPLETE_ATTRS + //8
          DOMXML_LOAD_SUBSTITUTE_ENTITIES + //4
          DOMXML_LOAD_DONT_KEEP_BLANKS //16 
        );
        if(is_object($dom))
        {
          $xpathXML = $dom->xpath_new_context();
          $xpresultXML = $xpathXML->xpath_eval("//persona", $xpathXML);
          $v_personas = $xpresultXML->nodeset;
          //preparamos los contadores
          $insertados = 0;
          $totales = 0;
          foreach($v_personas as $persona)
          {
            $nif = $persona->get_attribute('nif');
            $comprobacion = $this->consultar('SELECT count(1) as "cuenta" FROM tinv_xml where nif=\''.$nif.'\'');
            if($comprobacion[0]["cuenta"]==0)
            {
              $nombre = $persona->get_attribute('nombre');
              $apellidos = $persona->get_attribute('apellidos');
              $correcto = $this->operar("INSERT INTO tinv_xml values ('".$nif."','".$nombre."','".$apellidos."')");
              if($correcto!=-1)
                $insertados++;
            }
            $totales++;
          }
          $this->showMensaje('APL-26',array($insertados,$totales));
        }
        else {
          $this->showMensaje('APL-27');
        }
        $this->setResultadoBusqueda(array());
        $actionForward = $objDatos->getForward('correcto');
        break;
      default:
        die("La acción $str_accion no está activada para esta clase.");
    }
    return $actionForward;
  }

Como podemos ver, la aplicación espera un fichero XML del que extrae todos los tags <persona>. Si no existen en la BD los inserta con los atributos deseados.