HTML5 INSIGHT


Работа с бинарными данными с использованием типизированных массивов

Source: Working with Binary Data using Typed Arrays, Luke Hoban

Вместе с HTML5 в веб-разработку приходят новые API, расширяющие UX, привнося новые мультимедийные возможности и возможности взаимодействия в реальном времени. Зачастую этот функционал завязан на использование бинарных форматов файлов вроде MP3-аудио, PNG-изображений или MP4-видео. Использование бинарных файлов крайне важно в данном контексте, так как позволяет уменьшить требования к ширине канала, добиться необходимой производительности и вместе с этим оставаться совместимым с имеющимися технологиями. Еще недавно у веб-разработчиков не было прямого доступа к содержимому этих бинарных файлов или любых других бинарных форматов файлов.

В этой статье мы рассмотрим, как веб-разработчики могут снять этот барьер, используя Typed Arrays API для JavaScript, и использование нового API в демонстрационном примере Binary File Inspector на IE Test Drive.

Типизированные массивы, доступные в IE10 Platform Preview 4, позволяют веб-приложениями работать с широким спектром бинарных файлов и напрямую работать с двоичным контентом поддерживаемых браузером файлов. Поддержка Typed Arrays была добавлена по всему IE10: в JavaScript, в XMLHttpRequest, в File API и в Stream API.

Binary File Inspector

Пример Binary File Inspector показывает некоторые из новых возможностей, работающих при сочетании этих функций. Вы можете посмотреть ID3-заголовки музыкальных файлов, понять, как выглядит сырые данные в видео-файлах, а также посмотреть файлы других форматов вроде PCX-изображений, которые могут поддерживаться браузером с помощью JavaScript и Canvas.

Binary File Inspector

На примере выше .mp4-видео рендерится с помощью <video>-элемента слева, а бинарное содержимое файла отображается справа в шестнадцатеричном (HEX) формате и в виде соответствующих ASCII-символов. В этом примере вы можете увидеть несколько характерных для MPEG-файлов элементов вроде “ftyp” для “mp4”.

Typed Arrays и ArrayBuffers

Типизированные массивы предоставляют возможность взгялнуть на сырое бинарное содержимое через то или иное типизированное представление. Например, если мы хотим смотреть на бинарный поток данных как байтовый массив, мы можем использовать Uint8Array (Uint8 описывает 8-битовое беззнаковое целое значение, обычно называемое байтом). Если мы хотим считывать сырые данные как массив чисел с плавающей точкой, мы можем использовать Float32Array (Float32 описывает 32-битное число с плавающей точкой в соответствии со стандартом IEE754). Поддерживаются следующие типы:

Тип массиваРазмер элемента и описаниеInt8Array8-bit signed integerUint8Array8-bit unsigned integerInt16Array16-bit signed integerUint16Array16-bit unsigned integerInt32Array32-bit signed integerUint32Array32-bit unsigned integerFloat32Array32-bit IEEE754 floating point numberFloat64Array64-bit IEEE754 floating point number

Каждый тип массива — это представление для ArrayBuffer. ArrayBuffer — это ссылка на поток бинарных данных, но он не представляет никакого прямого способа для взаимодействия с данными. Создание TypedArray-представления для ArrayBuffer предоставляет доступ к чтению и записи бинарного содержимого.

Пример ниже создает новый ArrayBuffer с нуля и интерпретирует его содержимое различными способами:

// 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

Таким образом, типизированные массивы могут быть использованы для таких задач как создание значений с плавающей точкой из их по-байтовых компонент построения структур данных, требующих специфичной компоновки данных, исходя из соображений эффективности и совместимости.

Typed Arrays для чтения бинарных файлов

Важный новый сценарий, ставший возможным благодаря типизированным массивам, это чтение и отображение содержимого бинарных файлов, неподдерживаемых напрямую браузером. Вместе с различными типизированными массивами, описанными выше, Typed Arrays также предоставляет специальный объект DataView, который можно использовать для чтения и записи содержимого ArrayBuffer в неструктурированном виде. Это хорошо подходит для чтения новых форматов файлов, которые обычно построены поверх смешанных типов данных.

Binary File Inspector использует DataView для чтения PCX-файлов и их рендеринга с использованием <canvas>-элемента. Ниже приведена упрощенная версия того, что делается в демонстрационном примере для чтения заголовка файла, включая получения информации о ширине и высоте, DPI и глубине цвета (bits-per-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);

Код, аналогичный приведенному выше, может быть использован для добавления поддержки рендеринга в браузере широкого спектра новых типов файлов, включая собственные форматы изображений, дополнительные форматы видео-файлов и форматы данных, специфичных для той или иной области.

Получение двоичных данных через XHR и File API

Прежде, чем мы сможем использовать Typed Arrays API для работы с содержимым файлов, нам необходимо использовать соответствующие API браузера для получения доступа к сырым данным. Для доятупа к файлам с сервера XMLHttpRequest API был расширен с добавлением поддержки различных типов ответа (responseType). Так ответ в виде “arraybuffer” представляет содержимое ресурса, запрошенного с сервера, в виде ArrayBuffer-объекта для JavaScript. Также поддерживаются “blob”, “text” и “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();
}

Во многих случаях файлы могут выбираться пользователем, например, в виде приложения к письму в веб-клиенте для почты. File API предоставляет веб-разработчикам возможность чтения содержимого файлов, указанных через <input>-элемент, при перетаскивании (drag and drop) или из другого источника (Blob, File). Для чтения содержимого файла в ArrayBuffer используется объект FileReader и, подобно XHR-объекту, он используется асинхронно, чтобы убедиться, что чтение с диска не блокирует пользовательский интерфейс.

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);
}

Заключение

Бинарные данных активно используются веб-браузерами. С добавлением поддержки Typed Arrays, XHR2 и File API в IE10 веб-приложения теперь могут тоже напрямую работать с бинарными данными, манипулировать данными на по-байтовом уровне, отображать дополнительные бинырные форматы данных и извлекать дополнительные данные из существующих медиа-форматов. Попробуйте пример Binary File Inspector на IE Test Drive, и реализацию Typed Arrays в IE10.