DataTable to Excel
Esta mañana necesitaba una manera rápida y muy optima de exportar datos desde un dataset de una aplicación web a un archivo excel.
Esta es la solución que se me ocurrió.
Si Serializamos el DataTable a un archivo de texto plano, con las columnas separadas por tabulador y los registros por un salto de línea, lo guardamos con la extensión "xls" y lo abrimos, el excel lo reconoce inmediatamente y lo abre sin problemas.
Pros:
- Velocidad: Puedo serializar un dt de 10 columnas y 65535 filas que admite excel en 500 milisegundos.
- Sencillez: No hace falta ningún componente.
Contras:
- La hoja no estará formateada.
- No podemos decirle el Excel el tipo de datos de los campos.
La función que escribí es la que sigue:
/// <summary>
/// Serlializa el DataTable especificado en una cadena de texto separando
/// los valores de cada columna por un caracter de tabulación
/// y las filas por un salto de línea.
/// </summary>
/// <param name="dt">DataTable a Serializar</param>
/// <returns>Cadena con el DataTable serializado</returns>
private static string SerializeDT(DataTable dt)
{ const string FIELDSEPARATOR = "\t";
const string ROWSEPARATOR = "\n";
StringBuilder output = new StringBuilder();
// Escribir encabezados
foreach (DataColumn dc in dt.Columns)
{ output.Append(dc.ColumnName);
output.Append(FIELDSEPARATOR);
}
output.Append(ROWSEPARATOR);
// Escribir datos
foreach (DataRow item in dt.Rows)
{ foreach (object value in item.ItemArray)
{ output.Append(value.ToString());
output.Append(FIELDSEPARATOR);
}
// Escribir una línea de registro
output.Append(ROWSEPARATOR);
}
// Valor de retorno
return output.ToString();
}
Este sería el código para usarlo en una página aspx: El servidor devolverá los datos indicando al cliente que se trata de un archivo exel, y dependiendo de la configuración del navegador, se cargará directamente dentro de la misma ventana.
// Obtener los datos de la fuente que sea
// Bien una bd, un archivo, etc.
DataSet ds = GenerateData();
// Serializar los datos, usando nuestra función
string data = SerializeDT(ds.Tables[0]);
// Devolverlo al cliente, indicando que es un archivo xls
Response.ContentType = "application/vnd.ms-excel";
Response.Charset = "";
Response.Write(data);
Response.Flush();
Response.End();
Si en lugar de mostrar el resultado en la ventana del navegador preferimos que se muestre el díalogo de descarga de archivos, debemos guardar el resultado en un fichero del servidor, indicar en el Response que el lo que viene a continuación es para descargar y posteriormente sustituir el Response.Write del código anterior por un Response.WriteFile().
Creamos una sobrecarga para nuestra función de serialización, que nos permita persistir lo serializado en un archivo:
/// <summary>
/// Serlializa el DataTable especificado en una cadena de texto separando
/// los valores de cada columna por un caracter de tabulación
/// y las filas por un salto de línea.
/// </summary>
/// <param name="dt">DataTable a Serializar</param>
/// <param name="fileName">Archivo en el que se desean escribir los resultados</param>
private static void SerializeDT(DataTable dt, string fileName)
{ StreamWriter sw = new StreamWriter(fileName);
sw.Write(SerializeDT(dt));
sw.Close();
}
Modificamos el código de la página sustituyendo el Response.Write por Response.WriteFile:
// Obtener los datos de la fuente que sea
// Bien una bd, un archivo, etc.
DataSet ds = GenerateData();
// Serializar los datos, usando nuestra función
// No olvidar especificar la extensión xls para que el
// usuario lo abra con el Excel
const string FILE_NAME = "Datos.xls";
string exportFile = Server.MapPath(FILE_NAME);
SerializeDT(ds.Tables[0],exportFile);
// Devolverlo al cliente, indicando que es un archivo que debe ser descargado
Response.AppendHeader("content-disposition", "attachment; filename=" + FILE_NAME);Response.WriteFile(exportFile);
Response.End();
Espero que a alguno le sirva.