He sacado unos minutos para dejaros este breve manual sobre cómo leer y grabar un fichero de Excel en PHP. Hemos recibido el encargo de un proyecto que utiliza una hoja de Excel como fuente de datos y, aunque ya he trabajado otras veces con ficheros .csv e incluso generado ficheros .xls utilizando PHP, nunca había necesitado leer un fichero de Excel. Os explico cómo lo he hecho.
Leer archivos. xls de Excel
Para leer el archivo de Excel he utilizado la librería phpExcelReader, bajo licencia GNU. Para ello descargamos los archivos y guardamos la carpeta «Excel» en el directorio de nuestro proyecto. En la versión que yo he descargado he tenido que hacer una pequeña «chapuza» porque dentro del archivo reader.php había un require con una ruta equivocada. La versión que he subido a Github ya está corregida y funcionando correctamente.
Veamos un pequeño ejemplo de su uso. Leemos el fichero y mostramos en forma de tabla los datos que hemos obtenido.
<?php require_once 'Excel/reader.php'; $data = new Spreadsheet_Excel_Reader(); $data->setOutputEncoding('CP1251'); $data->read('fichero.xls'); echo("<table>"); for ($i = 1; $i <= $data->sheets[0]['numRows']; $i++) { echo("<tr>"); for ($j = 1; $j <= $data->sheets[0]['numCols']; $j++) { echo("<td>".$data->sheets[0]['cells'][$i][$j] ."</td>"); } echo("</tr>"); } echo("</table>");
A tener en cuenta
La variable $i controla las filas y la variable $j las columnas. Como generalmente los títulos de las columnas aparecen en la primera fila, si queremos obviarla tendremos que comenzar el for con $i=2 en lugar de con $i=1. De todas maneras, todo esto dependerá mucho del fichero Excel del que partamos.
En el ejemplo, para hacer referencia a un valor en concreto, por ejemplo, el almacenado en la celda B3, tendremos que utilizar
$celdaB3=$data->sheets[0]['cells'][2][3];
También cabe destacar que el objeto dispone de los campos numRows y numCols para almacenar el número total de filas y columnas respectivamente.
Si el archivo Excel cuenta con más de una hoja de cálculo, podemos acceder a cada una de ellas variando el índice de sheets:
$celdaB3=$data->sheets[1]['cells'][2][3];//Celda B3 de la hoja 1 $celdaB3=$data->sheets[1]['cells'][2][3];//Celda B3 de la hoja 2 $celdaB3=$data->sheets[2]['cells'][2][3];//Celda B3 de la hoja 3
Grabar ficheros Excel en PHP
Esta es la parte fácil. Para grabar un fichero de Excel solo necesitamos mostrar los datos en una tabla y cambiar la cabecera del archivo. Como en el ejemplo anterior ya utilicé la etiqueta table para hacer echo de los datos, solo nos quedaría cambiar las cabeceras justo antes.
<?php require_once 'Excel/reader.php'; $data = new Spreadsheet_Excel_Reader(); $data->setOutputEncoding('CP1251'); $data->read('fichero.xls'); //Establecemos las cabeceras para un archivo xls header('Content-type: application/vnd.ms-excel'); header("Content-Disposition: attachment; filename=excelenphp.xls"); header("Pragma: no-cache"); header("Expires: 0"); //Y mostramos los datos en forma de tabla echo("<table>"); for ($i = 1; $i <= $data->sheets[0]['numRows']; $i++) { echo("<tr>"); for ($j = 1; $j <= $data->sheets[0]['numCols']; $j++) { echo("<td>".$data->sheets[0]['cells'][$i][$j] ."</td>"); } echo("</tr>"); } echo("</table>");
Descargas
He subido los archivos del ejemplo a Github:
Hola David, solo para felicitarte y agradecerte la información. También hacer una consulta sobre el tema. Estoy leyendo un archivo XLS con fechas, pero al mostrarlas con tu ejemplo le suma un día a la fecha original. Sabes tu porque ? gracias de antemano!!
¡Hola! Es normal que te pase lo de la fecha. Tanto Excel como PHP comienzan la cuenta de años en 1900, pero Excel considera 1900 como año bisiesto (cuando en realidad no lo fue). Por eso las fechas de Excel cuentan un día más.
¡Gracias por tu comentario!
Gracias David … ayer googleando me encontre con «define(‘SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS’ , 25569);» esta definicion en el archivo reader.php; que es el numero de dias transcurridos entre 01 de junio de 1900 al 01 de junio de 1970 y como tu comentas si sumamos 1 dia que falta por el bisiesto fallido todo se arregla 🙂 y nos queda así «define(‘SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS’ , 25570);»
Saludo y un abrazo !
Cordial saludo David;
Sabes por que a la hora de ver la info del ecxel en la tabla no me muestra los caracteres especiales como las tilde y las ñ ?
Gracias!
Hola Manuel
La torre de Babel de las codificaciones es un mal que nos persigue, sobre todo a los no anglo parlantes. Sin tener más datos, solo te puedo recomendar que pruebes a jugar con las funciones utf8_encode y utf8_decode. Un saludo!
Muchas gracias por tu aporte David me sirvió mucho, lo incluí así:
echo utf8_encode(«» . $data->sheets[0][‘cells’][$i][$j] . «»);
Buenos días David, felicidades y gracias por la «guía» en relación a este tema.
Deseo ponerlo en práctica con una pequeña BBDD Mysql de la que debo extraer datos e importarlos a Excel.
Entiendo que en este caso, puedo realizar el mismo proceso pero no tengo fichero de entrada ($data->read(‘fichero.xls’); no se incluirá), y puedo utilizar los dos bucles que utilizas para la generación de la salida, informando en este caso
$data->sheets[0][‘cells’][$i][$j] = $valor_de_la_tabla;
en lugar de generar un echo.
¿Esto sería correcto o debo realizar algún cambio adicional?
Una vez más, felicidades
Un saludo
Hola Francesc
La verdad es que, revisado años después, el ejemplo de cómo grabar archivos en excel resulta un poco rocambolesco, ya que pretende grabar en formato excel información extraída de otro fichero excel, y eso podría llevar a confusión.
Para simplificarlo mucho, limítate a sacar la información de la base de datos, mostrarla mediante echo como si fuese una tabla y cambiar la cabecera para que el navegador sepa que se trata de un fichero excel.
Un saludo!
Buena noche, gracias por compartir, como puedo hacer pare leer celdas combinadas?
Gracias a ustedes, solucione el dato de la fecha aunque ahora me queda cuando la celda tiene el formato fecha/hora (08-03-2016 14:20:00) LA HORA me la muestra unas horas despues, me ayudas les agradesco ya eh probado formatos en la celda en excel pero no me da la exacta
Hola Jersson. Se me ocurre que pudiera ser problema de la configuración del uso horario en el servidor. Sin más datos poco más puedo decirte.
Hola he probado tu fuente incluso el ultimo fuente que adjuntaste el mas actualizado. y sale un error cuando hay una celda sin datos.
Tengo un excel que tiene registros pero solo cuando una celda no tiene dato sale un error.
Este error como se puede corregir.
Notice: Undefined offset: 5 in C:\xampp\htdocs\rjtm\carga_pagos_edyficar_01.php on line 18
Que tal como estas he probado y no permite archivos con la extension : xlsx, solo me permite abrir archivos con extension xls.
Tendras algun codigo mas actualizado que lea tambien archivos con extension xlsx
Uso desde hace mucho tiempo esta libreria con php 4.4.9 en un servidor de windows de 32 bits. Pero ahora tuve que migrar todo a php 5.3 en un servidor windows 64 bits y lo unico que no logro que fucione es la lectura de archivos excel me marca un error y ya baje otras librerias y sigue sin funcionar. Te gradeceria si tienes alguna información que me pueda llevar a solucionarlo. Estoy leyendo un excel de 2 filas, así que descarto que sea el tiempo de ejecución el cual ya he aumentado.
ERROR: PHP Fatal error: Maximum execution time of 60 seconds exceeded in C:\\wamp\\www\\sscm2\\Excel\\oleread.inc on line 67
Linea 67: $blocksToRead = min($this->numBigBlockDepotBlocks – $bbdBlocks, BIG_BLOCK_SIZE / 4 – 1);
Hola Diony. Así en frío, lo único que te podría decir son 2 cosas
– que aumentaras el tiempo de ejecución para intentar que se muestre el verdadero error (aunque seguramente sea un bucle infinito y no lo consigas)
– que busques una nueva versión actualizada de la librería.
Un saludo!
Buenas tardes,
ejecuto el scrip y me sale el siguiente mensaje:
The filename fichero.xls is not readable
he dado todos los permisos al directorio (777)
No se que puede fallar, ¿Podéis ayudarme???
Hola, gracias por el tuto, solamente tengo un inconveniente en el que espero me pueda ayudar, me aparece The filename fichero.xlsx is not readable, sea con fichero u otro tipo de nombre me falla, igual hago el cambio en el indesx y me sigue fallando, que podria ocacionar esto?, y como lo soluciono para que lo lea?, le agradesco su respuesta.
¡Hola! Verifica los permisos y el propietario del fichero.
Hola, ¿Qué tal? Estoy tratando de implementar tú solución pero no sé si en esta instrucción $data->read(‘fichero.xls’); se deba definir la ruta en la que tengo mi archivo, poner el archivo en una carpeta en especifica o qué se deba hacer.
Espero tu respuesta.
Gracias.
Hola Antonio! Puede deberse a 2 cosas: el archivo no está donde debería o está pero no tiene los permisos adecuados. Puedes probar a pasar como argumento la ruta absoluta del archivo (concatenándole antes del nombre la ruta completa hacia la carpeta que lo contiene) para descartar que nos estemos perdiendo por culpa de la ruta relativa. Revisa también que el archivo tenga permisos de lectura y que pertenezca al usuario adecuado.
Un saludo!