Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mobile Safari 10 IndexedDB Blobs

IndexedDB in Safari 10 supports blobs now. This works fine on desktop, however mobile Safari on iOS 10 throws an error:

UnknownError

and sometimes in combination:

TransactionInactiveError (DOM IDBDatabase Exception): Failed to store record in an IDBObjectStore:
The transaction is inactive or finished.

The code (shortened):

var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB,
    READ_WRITE = IDBTransaction && IDBTransaction.READ_WRITE ? IDBTransaction.READ_WRITE : 'readwrite',
    storeName = 'files',
    db;

init: function() {
    var request = indexedDB.open('mydb');
    request.onerror = ...;
    request.onupgradeneeded = function() {
        db = request.result;
        db.createObjectStore(storeName);
    };
    request.onsuccess = function() {
        db = request.result;
    };
},

save: function(id, data) {
    var put = function(data) {
            var objectStore = db.transaction([storeName], READ_WRITE).objectStore(storeName),
                request = objectStore.put(data, id);

            request.onerror = ...;
            request.onsuccess = ...;
        };

    // not all IndexedDB implementations support storing blobs, only detection is try-catch
    try {
        put(data);
    } catch(err) {
        if (data instanceof Blob) {
            Helpers.blobToDataURL(data, put);
        }
    }
}

On Mobile Safari 10 .put() doesn't throw like before, only later in the async error-callback.

Base64 strings work fine.

Bug in Mobile Safari or do I have to change code?

Test Case: http://fiddle.jshell.net/j7wh60vo/7/

like image 854
Benjamin E. Avatar asked Nov 03 '16 03:11

Benjamin E.


1 Answers

Ran across the same problem. Chrome 54 and Safari 10 work fine on desktop, but on Mobile Safari I kept getting the Unknown error when trying to store a Blob into IndexedDB. I can confirm that this really is just an issue with Blobs on Mobile Safari, and not some misuse of the API.

Fortunately, ArrayBuffers work fine. So I instead downloaded the images like:

xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';

Then saved them into IndexedDB as ArrayBuffers, and converted them to Blobs after pulling them out to get a url:

putRequest = objectStore.put(arrayBuffer, id);
putRequest.onsuccess = function(event) {
  objectStore.get(id).onsuccess = function(event) {
    var blob = new Blob([event.target.result], { type: 'image/jpeg'});
    var URL = window.URL || window.webkitURL;
    blobUrl = URL.createObjectURL(blob);
  };
};

I'd rather not have to convert ArrayBuffers to Blobs like this as I assume there is a performance penalty. But it works.

like image 87
Augustin Bralley Avatar answered Oct 04 '22 14:10

Augustin Bralley