Статьи

BLOB-объекты и многое другое: хранение изображений и файлов в IndexedDB

Желаемый будущий подход для хранения вещей на стороне клиента в веб-браузерах использует IndexedDB . Здесь я расскажу, как хранить изображения и файлы в IndexedDB, а затем представлю их через ObjectURL .

Общий подход

Во-первых, давайте поговорим о шагах, которые мы пройдем, чтобы создать базу данных IndexedDB, сохранить файл в нем, а затем прочитать его и представить на странице:

  1. Создать или открыть базу данных.
  2. Создать objectStore (если он еще не существует)
  3. Получить файл изображения в виде BLOB-объекта
  4. Инициировать транзакцию базы данных
  5. Сохранить этот блоб в базу данных
  6. Считайте этот сохраненный файл, создайте из него ObjectURL и установите его в качестве источника изображения на странице.

Создание кода

Давайте разберем все части кода, которые нам нужны для этого:

Создать или открыть базу данных.

// IndexedDB
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB,
    IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.OIDBTransaction || window.msIDBTransaction,
    dbVersion = 1;

// Create/open database
var request = indexedDB.open("elephantFiles", dbVersion);

request.onsuccess = function (event) {
    console.log("Success creating/accessing IndexedDB database");
    db = request.result;

    db.onerror = function (event) {
        console.log("Error creating/accessing IndexedDB database");
    };
    
    // Interim solution for Google Chrome to create an objectStore. Will be deprecated
    if (db.setVersion) {
        if (db.version != dbVersion) {
            var setVersion = db.setVersion(dbVersion);
            setVersion.onsuccess = function () {
                createObjectStore(db);
                getImageFile();
            };
        }
        else {
            getImageFile();
        }
    }
    else {
        getImageFile();
    }
}

// For future use. Currently only in latest Firefox versions
request.onupgradeneeded = function (event) {
    createObjectStore(event.target.result);
};

Предполагаемый способ использовать это — инициировать событие onupgradeneeded, когда база данных создается или получает более высокий номер версии. В настоящее время это поддерживается только в Firefox, но скоро будет в других веб-браузерах. Если веб-браузер не поддерживает это событие, вы можете использовать устаревший метод setVersion и подключиться к его событию onsuccess.

Создать objectStore (если он еще не существует)

// Create an objectStore
console.log("Creating objectStore")
dataBase.createObjectStore("elephants");

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

Получить файл изображения в виде BLOB-объекта

// Create XHR
var xhr = new XMLHttpRequest(),
    blob;

xhr.open("GET", "elephant.png", true);
// Set the responseType to blob
xhr.responseType = "blob";

xhr.addEventListener("load", function () {
    if (xhr.status === 200) {
        console.log("Image retrieved");
        
        // File as response
        blob = xhr.response;

        // Put the received blob into IndexedDB
        putElephantInDb(blob);
    }
}, false);
// Send XHR
xhr.send();

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

Инициировать транзакцию базы данных

// Open a transaction to the database
var transaction = db.transaction(["elephants"], IDBTransaction.READ_WRITE);

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

Сохранить этот блоб в базу данных

// Put the blob into the dabase
transaction.objectStore("elephants").put(blob, "image");

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

Считайте этот сохраненный файл, создайте из него ObjectURL и установите его в качестве источника изображения на странице.

// Retrieve the file that was just stored
transaction.objectStore("elephants").get("image").onsuccess = function (event) {
    var imgFile = event.target.result;
    console.log("Got elephant!" + imgFile);

    // Get window.URL object
    var URL = window.URL || window.webkitURL;

    // Create and revoke ObjectURL
    var imgURL = URL.createObjectURL(imgFile);

    // Set img src to ObjectURL
    var imgElephant = document.getElementById("elephant");
    imgElephant.setAttribute("src", imgURL);

    // Revoking ObjectURL
    URL.revokeObjectURL(imgURL);
};

Используйте ту же транзакцию, чтобы получить файл изображения, который вы только что сохранили, а затем создайте objectURL и установите для него значение src изображения на странице.
Например, это может быть файл JavaScript, который вы прикрепили к элементу скрипта, и тогда он будет анализировать JavaScript.

Полный код

Итак, вот полный рабочий код:

(function () {
    // IndexedDB
    var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB,
        IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.OIDBTransaction || window.msIDBTransaction,
        dbVersion = 1.0;

    // Create/open database
    var request = indexedDB.open("elephantFiles", dbVersion),
        db,
        createObjectStore = function (dataBase) {
            // Create an objectStore
            console.log("Creating objectStore")
            dataBase.createObjectStore("elephants");
        },

        getImageFile = function () {
            // Create XHR
            var xhr = new XMLHttpRequest(),
                blob;

            xhr.open("GET", "elephant.png", true);
            // Set the responseType to blob
            xhr.responseType = "blob";

            xhr.addEventListener("load", function () {
                if (xhr.status === 200) {
                    console.log("Image retrieved");
                    
                    // Blob as response
                    blob = xhr.response;
                    console.log("Blob:" + blob);

                    // Put the received blob into IndexedDB
                    putElephantInDb(blob);
                }
            }, false);
            // Send XHR
            xhr.send();
        },

        putElephantInDb = function (blob) {
            console.log("Putting elephants in IndexedDB");

            // Open a transaction to the database
            var transaction = db.transaction(["elephants"], IDBTransaction.READ_WRITE);

            // Put the blob into the dabase
            var put = transaction.objectStore("elephants").put(blob, "image");

            // Retrieve the file that was just stored
            transaction.objectStore("elephants").get("image").onsuccess = function (event) {
                var imgFile = event.target.result;
                console.log("Got elephant!" + imgFile);

                // Get window.URL object
                var URL = window.URL || window.webkitURL;

                // Create and revoke ObjectURL
                var imgURL = URL.createObjectURL(imgFile);

                // Set img src to ObjectURL
                var imgElephant = document.getElementById("elephant");
                imgElephant.setAttribute("src", imgURL);

                // Revoking ObjectURL
                URL.revokeObjectURL(imgURL);
            };
        };

    request.onerror = function (event) {
        console.log("Error creating/accessing IndexedDB database");
    };

    request.onsuccess = function (event) {
        console.log("Success creating/accessing IndexedDB database");
        db = request.result;

        db.onerror = function (event) {
            console.log("Error creating/accessing IndexedDB database");
        };
        
        // Interim solution for Google Chrome to create an objectStore. Will be deprecated
        if (db.setVersion) {
            if (db.version != dbVersion) {
                var setVersion = db.setVersion(dbVersion);
                setVersion.onsuccess = function () {
                    createObjectStore(db);
                    getImageFile();
                };
            }
            else {
                getImageFile();
            }
        }
        else {
            getImageFile();
        }
    }
    
    // For future use. Currently only in latest Firefox versions
    request.onupgradeneeded = function (event) {
        createObjectStore(event.target.result);
    };
})();

Поддержка веб-браузера

IndexedDB

Поддерживается с давних пор (несколько версий назад) в Firefox и Google Chrome.
Планируется быть в IE10, неясно про Safari и Opera.
onupgradeneeded

Поддерживается в последнем Firefox.
Планируется скоро появиться в Google Chrome и, надеюсь, в IE10. Непонятно о Сафари и Опере.
Хранение файлов в IndexedDB

Поддерживается в Firefox 11 и более поздних версиях.
Планируется поддержка в Google Chrome. Надеюсь, IE10 его поддержит. Непонятно о Сафари и Опере.
XMLHttpRequest Уровень 2

Safari 5+ давно поддерживается в Firefox и Google Chrome, планируется в IE10 и Opera 12.
responseType «blob»

В настоящее время поддерживается только в Firefox.
Скоро будет в Google Chrome и планируется в IE10. Непонятно о Сафари и Опере.

Демо и код

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

Код для хранения файлов в IndexedDB также доступен на GitHub, так что играйте сейчас!