Manipulación de datos binarios con Typed Arrays

Con HTML5 vienen muchas APIs que abren la puerta a experiencias de usuario con contenidos multimedia y comunicaciones en tiempo real. Con frecuencia estas funcionalidades dependen de formatos de archivo binarios, como el audio MP3, las imágenes PNG o el vídeo MP4. El uso de formatos de archivo binario es importante en estos casos para reducir el consumo de ancho de banda, conseguir el rendimiento esperado e interactuar con los formatos de archivo actuales. Pero hasta hace poco los desarrolladores Web no tenían acceso directo a los contenidos de este tipo de archivos ni al de ningún otro formato binario.

En este artículo vamos a ver cómo los desarrolladores Web pueden ya superar las barreras del formato binario con la API de JavaScript llamada Typed Arrays (“arrays con tipo”) y exploraremos su utilización en una demo de Test Drive llamada Binary File Inspector.

Typed Arrays, disponible en la Platform Preview 4 de IE10, permite a las aplicaciones Web utilizar una amplia gama de formatos de archivo binarios y manipular directamente los contenidos de archivos binarios ya soportados por el navegador. El soporte para Typed Arrays afecta a todo el conjunto de IE10: en JavaScript, en XMLHttpRequest, en el API File y también en el API Stream.

Demo “Binary File Inspector”

La demo de test Drive Binary File Inspector pone de manifiesto algunas de las nuevas capacidades que ofrece esta combinación de funcionalidades nuevas. Podemos ver las cabeceras ID3 de los archivos de música, rebuscar directamente entre los contenidos de los archivos de vídeo y también podemos ver cómo otros formatos de archivo, como el formato de imágenes PCX, pueden utilizarse en el navegador empleando para ello el elemento Canvas y JavaScript.

clip_image002

En el ejemplo anterior aparece reproduciéndose un vídeo .mp4 dentro de un elemento <video> a la izquierda, y el contenido binario del archivo aparece a la derecha, en formato HEX y los caracteres ASCII correspondientes. En este ejemplo podemos ver algunos elementos característicos del formato de archivo MPEG, como el “ftyp” identificado como “mp4”.

Typed Arrays y ArrayBuffers

Typed Arrays nos ofrece un medio para ver el contenido binario plano de un archivo mediante alguna vista específica adaptada a un tipo de datos. Por ejemplo, si queremos ver los datos binarios de nuestro archivo de ejemplo en un momento dado, podemos utilizar un Uint8Array (Uint8 significa un valor de entero sin signo de 8 bits, que es lo que habitualmente se conoce como byte). Si lo que queremos es leer los datos binarios en forma de array de números de coma flotante, podemos emplear el tipo Float32Array (Float32 es el tipo de datos de 32 bits especificado en IEEE754 para representar valores de coma flotante). Están soportados los siguientes tipos de datos:

Tipo de array

Tamaño y descripción del elemento

Int8Array

Entero con signo de 8 bits

Uint8Array

Entero sin signo de 8 bits

Int16Array

Entero con signo de 16 bits

Uint16Array

Entero sin signo de 16 bits

Int23Array

Entero con signo de 32 bits

Uint32Array

Entero sin signo de 32 bits

Float32Array

Número de coma flotante de 32 bits IEEE754

Float64Array

Número de coma flotante de 64 bits IEEE754

Cada tipo de array es una vista aplicada sobre un objeto ArrayBuffer. El ArrayBuffer es una referencia al dato binario original, pero no supone ninguna vía directa para interactuar con los datos. La creación de una vista TypedArray sobre el ArrayBuffer nos aporta acceso en modo lectura y escritura sobre los contenidos binarios.

El ejemplo siguiente crea un nuevo ArrayBuffer desde cero e interpreta sus contenidos de varias formas distintas:

// Create an 8 byte buffer

var buffer = new ArrayBuffer(8);

// View as an array of Uint8s and put 0x05 in each byte

var uint8s = new Uint8Array(buffer);

for (var i = 0; i < 8; i++) {

    uint8s[i] = 5; // fill each byte with 0x05

}

// Inspect the resulting array

uint8s[0] === 5; // true - each byte has value 5

uint8s.byteLength === 8; // true - there are 8 Uint8s

// View the same buffer as an array of Uint32s

var uint32s = new Uint32Array(buffer);

// The same raw bytes are now interpreted differently

uint32s[0] === 84215045 // true - 0x05050505 == 84215045

Así, Typed Arrays nos sirve para tareas como la creación de valores de coma flotante a partir de sus componentes a nivel de byte o para generar estructuras de datos que requieren una capa de datos muy específica, en favor de la eficiencia o interoperabilidad.

Typed Arrays para leer formatos de archivo binarios

Uno de los escenarios más interesantes que nos abre Typed Arrays es la posibilidad de leer y presentar en pantalla el contenido de formatos de archivo binarios no soportados de forma nativa por el navegador. Al igual que con los distintos tipos de arrays que comentábamos antes, Typed Arrays nos ofrece también un objeto DataView que podemos utilizar para leer y escribir los contenidos en un ArrayBuffer de manera no estructurada. Es una opción bastante eficaz para leer nuevos formatos de archivo, que habitualmente están formados por mezclas variopintas de datos de diferentes tipos.

La demo Binary File Inspector utiliza el DataView para leer el formato de archivo PCX y mostrarlo en pantalla dentro de un elemento <canvas>. Aquí tenemos una versión algo simplificada de lo que hace la demo para leer la cabedera del archivo, en donde se incluye información como la anchura, altura, DPI y profundidad del color en bits por pixel.

var buffer = getPCXFileContents();

var reader = new DataView(buffer);

// Read the header of the PCX file

var header = {}

// The first section is single bytes

header.manufacturer = reader.getUint8(0);

header.version = reader.getUint8(1);

header.encoding = reader.getUint8(2);

header.bitsPerPixel = reader.getUint8(3);

// The next section is Int16 values, each in little-endian

header.xmin = reader.getInt16(4, true);

header.ymin = reader.getInt16(6, true);

header.xmax = reader.getInt16(8, true);

header.ymax = reader.getInt16(10, true);

header.hdpi = reader.getInt16(12, true);

header.vdpi = reader.getInt16(14, true);

Un código similar a éste se podría utilizar para la restitución de una amplia gama de formatos de datos nuevos en el navegador, e incluso formatos propios de imagen, nuevos formatos de vídeo o formatos de datos de mapas específicos de un dominio.

Lectura de datos binarios con XHR y el File API

Antes de que pongamos las APIs de Typed Arrays a trabajar con los contenidos de nuestros archivos necesitamos que las APIs del navegador puedan acceder a los datos en bruto. Para acceder a los archivos desde el servidor, se ha ampliado el API XMLHttpRequest con soporte para diversos valores “responseType”. El responseTypearraybuffer” nos devuelve el contenido del recurso solicitado de servidor para utilizarlo desde JavaScript en forma de un objeto ArrayBuffer. También están soportados los tipos de respuesta “blob”, “text” y “document”.

function getServerFileToArrayBufffer(url, successCallback) {

    // Create an XHR object

    var xhr = new XMLHttpRequest();

    xhr.onreadystatechange = function () {

        if (xhr.readyState == xhr.DONE) {

            if (xhr.status == 200 && xhr.response) {

  // The 'response' property returns an ArrayBuffer

                successCallback(xhr.response);

            } else {

                alert("Failed to download:" + xhr.status + " " + xhr.statusText);

            }

        }

    }

    // Open the request for the provided url

    xhr.open("GET", url, true);

    // Set the responseType to 'arraybuffer' for ArrayBuffer response

    xhr.responseType = "arraybuffer";

    xhr.send();

}

Muchas veces los archivos los aporta el propio usuario, por ejemplo al añadir documentos adjuntos a un correo en una aplicación de webmail. El API File proporciona herramientas para leer el contenido de los archivos entregados mediante un elemento <input>, mediante una operación drag-and-drop o bajo cualquier otra actividad capaz de entregar archivos binarios o de texto. El objeto FileReader se utiliza para leer el contenido de un archivo y volcarlo dentro de un ArrayBuffer y, como ocurre con el objeto XHR, es asíncrono con el fin de garantizar que la lectura desde el disco no bloquea o impide la respuesta desde la interfaz de usuario.

function readFileToArrayBuffer(file, successCallback) {

    // Create a FileReader

    var reader = new FileReader();

    // Register for 'load' and 'error' events

    reader.onload = function () {

        // The 'result' property returns an ArrayBuffer for readAsArrayBuffer

        var buffer = reader.result;

        successCallback(buffer);

    }

    reader.onerror = function (evt) {

        // The error code indicates the reason for failure

        if (evt.target.error.code == evt.target.error.NOT_READABLE_ERR) {

            alert("Failed to read file: " + file.name);

        }

    }

    // Begin a read of the file contents into an ArrayBuffer

    reader.readAsArrayBuffer(file);

}

Conclusión

Los datos binarios son de uso muy frecuente en navegadores Web. Con el soporte para Typed Arrays, XHR2 y el API File en IE10, las aplicaciones Web ahora pueden trabajar directamente sobre los datos binarios, para manipularlos a nivel de byte, para restituir en la interfaz de usuario nuevos formatos de datos binarios y para extraer datos de los archivos multimedia actuales. Puedes probar la demo de test Drive llamada Binary File Inspector y darle una vuelta a las funcionalidades de Typed Arrays en IE10.

FUENTE: Luke Hoban - https://blogs.msdn.com/b/ie/archive/2011/12/01/working-with-binary-data-using-typed-arrays.aspx

 

Saludos,

El equipo de MSDN España