Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The mysterious case of disappearing blobs in Chrome (in IndexedDB)

Summary

What I am trying to do is pretty simple:

  • 1a. If an image is not a local store of some sort (such as IndexedDB), read the image as a byte array from the server, put in local store (as a byte array or a reference to a file, I don't care)
  • 1b. If an image is in the local store, read the byte array from local store. Show this byte array as an image in an html page.

Somehow, between Blobs, objectURLs, indexedDB and caching it has all gotten overly complicated and is exhibiting some strange behaviour. If there was a way to stick an ArrayBuffer directly into an image rather than first converting to a Blob then an ObjectURL, then I would probably go with that as it is simpler and gets rid of the problematic Blobs and a few unnecessary steps.

If you would like to see a code sample of the flow then see this jsfiddle. Note that, as described below, the problem does not occur in the jsfiddle sample (for some reason that I can't figure out).

There is a reason that I am using IndexedDB rather than relying on browser caching, so lets try to avoid that discussion and has no bearing on the fact that IndexedDB seems to be misbehaving on Chrome.

I am interested if anyone else has encountered something similar or any suggestions as how to improve the situation.

Details

Chrome version 38.0.2125.104 m.

Basically, the flow is to check if blob is in IndexedDB by index (see jsfiddle for reference):

  • 1a. If it is not, then retrieve blob from server (xhr.open), put the blob in IndexedDB (objectStore.put) and show the blob (imgSrc = createTheObjectUrl(blob)).
  • 1b. If it is, then retrieve the blob from IndexedDB (objectStore.get), create URL from blob, set image src to URL.

The problem is that it works initially, but after a short while (sometimes when I refresh the page, sometimes when I close Chrome and return to the web page) I get a 404 (not found) when accessing the URL for the blob.

There are a few things to note: -

  1. In my limited tests with other browsers I do not see the same behaviour - other browsers seem to work fine.
  2. When I view the blob-internals page (chrome://blob-internals) there is a path to a blob on my disk. When it is working (image is visible), that file exists, when it starts to fail (404 Not Found) that file does not exist (even though blob-internals still references it).
  3. When I tried to reproduce this in jsfiddle, the problem does not happen. This is a cut and paste from my code. Unfortunately, I don't have a public version of this web server, so I can't show it failing.

Given that jsfiddle always seems to work, all I can think is that there is something different about the way that my server is configured. I had a look at the difference in the headers being returned and I can see that in the case for jsfiddle, caching is enabled. So, I started thinking that this has something to do with caching (which could be a completely wrong assumption). It is as though Chrome is tracking the usage of the blob and deleting it from the file system as soon it goes out of scope which results in an entry in IndexedDB without a file (which in itself seems like a bug). I don't want to enable caching on the server or have the lifetime of the blob dependent on server cache settings.

Workaround

As a workaround I did the following: -

  • 1a. If the image is not in IndexedDB, then retrieve it as a Blob from the server. Convert Blob to ArrayBuffer. Store in IndexedDB as ArrayBuffer.
  • 1b. If the image is in IndexedDB then retrieve, convert ArrayBuffer to Blob, create URL from Blob, set image src to URL.

This is not ideal as it means that I have an added overhead of reading the arraybuffer from the blob when first showing the image (before it is stored in IndexedDB) and then I have the overhead of reading the ArrayBuffer into a blob when retrieving from IndexedDB. Perhaps there is some clever shared resourcing going on which means that they use the same underlying buffer, but that means relying on the implementation for performance.

There is more - if I create a new Blob from the Blob that is returned by the server or create an ArrayBuffer from the Blob, then a new Blob from the ArrayBuffer it still does not work. It is as though there is some kind of shared reference counted resource being used. That is - any workaround I can think of that involves storing a blob in IndexedDB does not work.

like image 561
acarlon Avatar asked Oct 27 '14 03:10

acarlon


1 Answers

Seems like a bug in chrome please see: https://code.google.com/p/chromium/issues/detail?id=108012#c162

chrome canary doesn't suffer from it

like image 110
meirm Avatar answered Sep 30 '22 21:09

meirm