11/1/07

csv

Los ficheros CSV están muy bien. Son de lo más simple que hay: están en texto plano, sin codificaciones extrañas, son fáciles de entender para cualquier persona y cómodos de interpretar por la máquina.

Sin embargo tienen un problema al tratar con los números. En Estados Unidos y en muchos otros países angloparlantes utilizan el "." como separador de decimales y la "," como separador de miles. En Europa lo hacemos al revés.
Así un americano diría que un euro equivale a 166.386 pesetas, mientras que un europeo diría que son 166,386

esto en un csv clásico se plasmaría de la siguiente manera:

€,pta
1,166.386

o bien

€,pta
1,"166,386"

El problema viene cuando un editor de hojas de cálculo intenta abrir el fichero. En nuestro caso, el excel 2003 mira en el sistema qué caracteres tenemos definidos para separar las listas, los decimales, y los miles. En mi caso tengo: ";" para separar las listas, "," para los decimales y "." para los miles. Resultado: ninguno de los dos ficheros csv de arriba me los abriría correctamente.

si lo hubiera puesto de la siguiente manera:
€;pta
1;166,386

hubiera funcionado a la perfección.

Si tenemos un sistema que exporta datos en csv habría que preguntarle al usuario qué separadores usa en su sistema para que todo funcione correctamente, lo cual es un engorro.

Si permitimos al usuario cargar datos a partir de ficheros csv nos ocurre lo mismo, aunque aquí podemos hacer la siguiente comprobación, que funciona en la mayor parte de los casos. Comparamos el número de "," y de ";" que hay en la primera fila. Si es superior el número de "," nos encontramos en un sistema anglosajón y si hay más ";" asumimos que es europeo. Evidentemente es una comprobación muy burda, pero eficaz en la mayoría de los casos.

Subir ficheros con AJAX

De lo que he visto por ahí la mejor biblioteca de javascript es prototype. La llevo usando desde la versión 1.3 y la verdad es que va de lujo. Es cierto que en las siguientes versiones ha crecido quizá por encima de lo deseable, pero sigue siendo la mejor.

Para enviar datos de formularios utiliza la función encodeURIComponent, que es perfecta para todos los tipos de campo, excepto para los ficheros. Estaría bien que codificara el fichero, pero supongo que por razones de seguridad no lo hace.

Para poder hacerlo tenemos que recurrir a otra "versión de AJAX", que no es con el objeto XMLHttpRequest, sino con el tradicional uso de IFRAMES ocultos.

Aquí van los pasos a seguir

1. Creamos un IFRAME oculto que va a ser nuestro "objeto de entrada / salida":

var frameId='frameId',f;
if(window.ActiveXObject) //en IE hay que hacerlo así
{
f=document.createElement('<iframe id="' + frameId + '" name="' + frameId + '">');
f.src='javascript:false';
}
else
{
f=document.createElement('iframe');
f.id=frameId;
f.name=frameId;
}
f.style.position = 'absolute';
f.style.top = '-1000px';
f.style.left = '-1000px';
document.body.appendChild(f);


2. Creamos un FORM, redirigiendo la salida al IFRAME y le añadimos el input de tipo FILE

var f=document.createElement('form');
f.action=url;
f.method='POST';
f.target=frameId;
if(f.encoding)
f.encoding='multipart/form-data';
else
f.enctype='multipart/form-data';
f.style.position = 'absolute';
f.style.top = '-1000px';
f.style.left = '-1000px';

input.name='file';
f.appendChild(input);
document.body.appendChild(f);


3. Disparamos el método submit() del FORM
f.submit();

Si queremos comprobar qué nos devuelve el servidor cuando le enviamos el fichero tendríamos que añadir una función de respuesta cuando se cargara el IFRAME.

Haciéndolo un poco más orientado a objetos me he creado una clase Ajax.FileUpload para usarla como si fuera parte de la biblioteca prototype

Hay que tener en cuenta que uso un input que ya tengo en la página y que cuando envíe el fichero ya no lo voy a usar para nada más. El problema es que los inputs de tipo file tienen una serie de limitaciones de seguridad. Por ejemplo, en IE no se les puede asignar el atributo value, con lo cual estás vendido, además cuando los intentas clonar, no te copia el value del input original. Sólo te quedan dos opciones: o lo que hago yo que es usar el input original, o bien como se hace en el google page creator y en el correo yahoo, que es crear el form al generar la página, y posicionarlo donde te interesa.