Exportando un JSON a CSV con JavaScript.

Aprende cómo convertir JSONs sencillos a CSVs de forma fácil con JavaScript y después exportarlos para que tu usuario pueda descargarlo en su mismo navegador.

Como de costumbre, daré el contexto de la publicación. Hace unos días me tocó tratar de acomodar un servicio que exportaba 100 a 200k (aprox) de filas a un Excel y fue bastante horrible, ¿por qué?, para empezar el servicio era un CRON que vivía donde vivía la plataforma del usuario, es decir, yo visitaba su sitio, solicitaba los datos para exportar y el mismo sitio manejaba un CRON que corría de fondo y este hacía el export para mandarlo por correo.

Había 5 problemáticas aquí:

  • El servicio como dije estaba anidado, si se comía los recursos y ahora otro usuario visitaba el sitio y se estaba ejecutando el CRON entonces el servicio se caía.
  • Si yo exportaba grandes cantidades de datos pasaban los 25 megas y ya no me podían llegar por correo.
  • Usaban un servidor muy chico para este export (2gb de RAM, 1 vCPU).
  • El lenguaje era PHP y era una versión vieja (5.6)
  • La base de datos estaba viéndose muy afectada por cargas constantes de datos (solicitudes de clientes + estos exports) en un MySQL viejo (5.5)

¿Qué pasa entonces?. Bueno, para empezar hay que saber entender las bondades que nos ofrecen hoy en día los navegadores y empezar a entender cuándo dejarles la carga al back-end y cuándo al front-end, así como saber qué herramientas utilizar.

Excel es bueno cuando se trata de usarlo como programa directo pero tiene sus limitantes como cierta cantidad de filas, columnas, carga lenta con archivos pesados, es privativo, etc…¿Entonces qué alternativa hay?, el CSV.

CSV proviene de “Comma-separated values” (o valores separados por coma en español), y es así, tal cual, un archivo de texto plano donde los valores se separan por coma.

En este caso veremos un código que nos permitirá hacer que un JSON se vuelva CSV.

Lo primero es tener la función que convierta a CSV:

/**
 *
 * @param objArray
 */
function convertToCSV(objArray) {
 const array = typeof objArray != "object" ? JSON.parse(objArray) : objArray;
 let str = "";
for (let i = 0; i < array.length; i++) {
  let line = "";
  for (let index in array[i]) {
   if (line != "") line += ",";
line += array[i][index];
  }
str += line + "\r\n";
 }
return str;
}

Esta función lo que hará entonces será convertir nuestra información a strings planas.

En otras palabras algo como { “miCampo”: “valor” } -> miCampo, valor (este es un ejemplo así no se vería en la versión final).

Ahora bien, ya tenemos nuestra función que convierte a CSV, ahora necesitamos exportar:

/**
 *
 * @param headers
 * @param items
 * @param fileName
 */
function exportCSVFile(headers, items, fileName) {
 if (headers) {
  items.unshift(headers);
 }
const jsonObject = JSON.stringify(items);
const csv = convertToCSV(jsonObject);
const exportName = fileName + ".csv" || "export.csv";
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
 if (navigator.msSaveBlob) {
  navigator.msSaveBlob(blob, exportName);
 } else {
  const link = document.createElement("a");
  if (link.download !== undefined) {
   const url = URL.createObjectURL(blob);
   link.setAttribute("href", url);
   link.setAttribute("download", exportName);
   link.style.visibility = "hidden";
   document.body.appendChild(link);
   link.click();
   document.body.removeChild(link);
  }
 }
}

Esta función lo que hará entonces es generarnos un link de descarga o hacer la descarga a través del blob basados en la información que le damos del CSV y las cabeceras.

¿Qué son las cabeceras?, como en Excel sería un identificador para que sepamos de qué va la columna y es un valor único, es decir:

[{ “miCabecera”: “Identificador” }, { “miValor”: “1” }, { “miValor”: “2”} ]

Y pues el nombre de nuestro archivo. Este nombre no es requerido, pero, entre más opciones, mejor, ¿no?.

Aquí viene una problemática que yo sugiero solventar desde la generación del JSON, que es que retiren toda comilla, salto de línea (\n), comas y demás en las strings generadas desde Back o tengan una función que haga esta limpieza, ya que si por ejemplo hay una frase como: “Hola, ¿cómo estás?”, la coma hará que se considere como “otra columna” haciendo que, “Hola” quede como una columna y “¿cómo estás?” como otra.

Ahora hay un último detalle que también se podría considerar y es que se deben limpiar las strings para que también sean válidas. Por ejemplo en esta respuesta de StackOverflow nos muestra un ejemplo (https://stackoverflow.com/a/49950777/5032157):

var cCode,
 bArr = [];
bArr.push(255, 254);
for (var i = 0; i < csv.length; ++i) {
 cCode = csv.charCodeAt(i);
 bArr.push(cCode & 0xff);
 bArr.push((cCode / 256) >>> 0);
}

Aquí ya nos permite hacer que todo sea “legítimo” recorriendo la string de CSV y entonces acomode toda esa información.

En mi caso no lo implementaré, se los dejo de tarea estos casos superiores, ustedes deciden dónde manejarlos.

Así que tomando nuestras dos funciones con un ejemplo, tendremos:

const headers = {
 id: 'Identificador',
 nombre: 'Nombre'
};
const data = [
 { id: 1, nombre: 'John Doe' },
 { id: 2, nombre: 'Juan' },
 { id: 3, nombre: 'Samanta' }
];
exportCSVFile(headers, data, 'nombres');

Y nos descargará un archivo CSV con el nombre: “nombres.csv”, al cual veríamos algo así:

Cabe mencionar por último que al abrirlo en excel tenemos las mismas limitantes bases (límite de columnas/filas por ejemplo) pero, un CSV es legible por múltiples softwares que no nos forzan a tener todos esos límites y es mucho más rápido y eficaz a tal grado que, como podemos ver podemos hacer uso del navegador de nuestro cliente para ahorrarle carga al servidor.

¿Fácil no es así?

¿Cuál es tu reacción?
+1
0
+1
0
+1
0
+1
0
+1
0
Total
0
Shares
Publicación anterior

Categorizando textos con inteligencia artificial en NodeJS

Siguiente Publicación

Creando un componente de barra lateral (sidebar) reutilizable en Angular

Publicaciones Relacionadas