diachedelic / capacitor-blob-writer

Capacitor plugin to write binary data to the filesystem
MIT License
132 stars 17 forks source link

Web: Use blob storage for indexedDB #42

Closed Azarattum closed 2 years ago

Azarattum commented 2 years ago

Using base64 for binary data results in 33% size overhead. This is unacceptable for my use case...

Also, if we use blobs, a valid blob URI can be returned from write_blob. The current README example does not work for the web platform right now, since the source url is invalid.

diachedelic commented 2 years ago

This is a good idea. Can you implement it?

Azarattum commented 2 years ago

@diachedelic, sure I'll give it a try when I have some time.

diachedelic commented 2 years ago

I just realised that using IndexedDB would break this plugin's compatibility with the Filesystem plugin. For example, Filesystem.getUri does not return an IndexedDB URI.

I think that if you wanted to use IndexedDB for blob storage in the browser, that functionality is easily written in JavaScript and does not require a plugin.

Azarattum commented 2 years ago

@diachedelic I actually did some tests. It is possible to store blob values inside objects in IndexedDB. That means that achieving full compatibility with Filesystem plugin is possible!

How existing Filesystem works: It stores a JS object for each file entry in IndexedDB named Disc when in the browser environment.

{
  content: "test",
  ctime: 1655189430015,
  folder: "",
  mtime: 1655189430015,
  path: "/file.dat",
  size: 4,
  type: "file",
}

Meaning that if we would be able to replace the content property with a blob, everything will just work. And we actually can do that!

const blob = ...; //Obtain a blob
indexedDB.open("Disc").onsuccess = ({ target }) => {
  const db = (target as any).result as IDBDatabase;
  const store = db
    .transaction("FileStorage", "readwrite")
    .objectStore("FileStorage");

  store.add({
    path: "/blob.dat",
    folder: "",
    type: "file",
    size: blob.size,
    ctime: Date.now(),
    mtime: Date.now(),
    content: blob, //We put our blob as the content
  });
};

Then, when trying to use Filesystem APIs, they work just fine:

console.log(await Filesystem.stat({ path: "blob.dat" }));
// {type: 'file', size: 2499401, ctime: 1655190689721, mtime: 1655190689721, uri: '/blob.dat'}
console.log(await Filesystem.readFile({ path: "blob.dat" }));
// {data: Blob}

Therefore, I think this issue should be reopened, considering this implementation. Meanwhile, I will start working on the PR in my spare time.

P.S. Also, I think this implementation might be beneficial for performance and might significantly improve the result in the last (browser) benchmark table.

diachedelic commented 2 years ago

Fantastic!