Export and Import Database
Export / Import IndexedDB <---> Blob
The npm package dexie-export-import extends Dexie with the capability to export and import databases to and from Blobs.
Install
Usage
Here's the basic usage. There's a lot you can do by supplying optional [options]
arguments. The available options are described later on in this README (See Typescript interfaces below).
Note that you can also import the package as follows:
NOTE: Typescript users using dexie@2.x will get compilation errors if using the static import method Dexie.import()
.
Sample
Here's a working sample on CodePen. It uses downloadjs to deliver the blob as a "file download" to the user. For receiving an import file, it uses a drop area where you can drop your JSON file. Click the Console tab in the bottom to see what progressCallbacks receive.
Even though this sample doesn't show it, blobs can also be sent or retrieved to/from a server, using the fetch API.
Here's a blog article on how to export IndexedDB from DevTools on an arbitrary web page or web app, by dynamically including dexie and dexie-export-import in the devtools console.
Features
Export of IndexedDB Database to JSON Blob.
Import from Blob back to IndexedDB Database.
An import Blob can be retrieved from an URL (using fetch()) or from a user-input file (dropped or browsed to).
An export Blob can be either given end-user to be stored in Downloaded Files, or be send to a server over HTTP(S) using fetch().
Chunk-wise / Streaming - does not read the entire DB into RAM
Progress callback (typically for showing progress bar)
Optional filter allows to import/export subset of data
Support for all structured cloneable exotic types (Date, ArrayBuffer, Blob, etc) except CryptoKeys (which by design cannot be exported)
Atomic - import / export within one database transaction (optional)
Export speed: Using getAll() in chunks rather than openCursor().
Import speed: Using bulkPut() in chunks rather than put().
Can well be run from a Web Worker (better speed + doesn't lock GUI).
Can also export IndexedDB databases that was not created with Dexie.
Compatibility
dexie
^2.0.4, ^3.x, ^4.x
Safari
^10.1
IE
11
Edge
any version
Chrome
any version
FF
any version
Similar Libraries
Much smaller in size, but also much lighter than dexie-export-import.
Indexeddb-export-import can be better choice if:
your data contains no Dates, ArrayBuffers, TypedArrays or Blobs (only objects, strings, numbers, booleans and arrays).
your database is small enough to fit in RAM on your target devices.
Dexie-export-import was build to scale when exporting large databases without consuming much RAM. It does also support importing/exporting exotic types.
Interface
Importing this module will extend Dexie and Dexie.prototype as follows. Even though this is conceptually a Dexie.js addon, there is no addon instance. Extended interface is done into Dexie and Dexie.prototype as a side effect when importing the module.
StaticImportOptions and ImportOptions
These are the interfaces of the options
optional arguments to Dexie.import() and Dexie.prototype.import(). All options are optional and defaults to undefined (falsy).
ImportProgress
This is the interface sent to the progressCallback.
ExportOptions
This is the interface of the options
optional arguments to Dexie.prototype.export(). All options are optional and defaults to undefined (falsy).
ExportProgress
This is the interface sent to the ExportOptions.progressCallback.
Defaults
These are the default chunk sizes used when not specified in the options object. We allow quite large chunks, but still not that large (1MB RAM is not much even for a small device).
JSON Format
The JSON format is described in the Typescript interface below. This JSON format is streamable as it is generated in a streaming fashion, and imported also using a streaming fashion. Therefore, it is important that the data come last in the file.
Example JSON File
Exporting IndexedDB Databases that wasn't generated with Dexie
As Dexie can dynamically open non-Dexie IndexedDB databases, this is not an issue. Sample provided here:
Background / Why
This feature has been asked for a lot:
https://github.com/dexie/Dexie.js/issues/391
https://github.com/dexie/Dexie.js/issues/99
https://stackoverflow.com/questions/46025699/dumping-indexeddb-data
My simple answer initially was this:
Looks simple!
But:
The whole database has to fit in RAM. Can be issue on small devices.
If using JSON.stringify() / JSON.parse() on the data, we won't support exotic types (Dates, Blobs, ArrayBuffers, etc)
Not possible to show a progress while importing.
This addon solves these issues, and some more, with the help of some libraries.
Libraries Used
To accomplish a streamable export/import, and allow exotic types, I use the libraries listed below. Note that these libraries are listed as devDependencies because they are bundles using rollupjs - so there's no real dependency from the library user perspective.
These modules enables something similar as JSON.stringify() / JSON.parse() for exotic or custom types.
This module allow to read JSON in a streaming fashion
Streaming JSON
I must admit that I had to do some research before I understood how to accomplish streaming JSON from client-side Javascript (both reading / writing). It is really not obvious that this would even be possible. Looking at the Blob interface, it does not provide any way of either reading or writing in a streamable fashion.
What I found though (after some googling) was that it is indeed possible to do that based on the current DOM platform (including IE11 !).
Reading JSON in Chunks
A File or Blob represents something that can lie on a disk file and not yet be in RAM. So how do we read the first 100 bytes from a Blob without reading it all?
Ok, and in the next step we use a FileReader to really read this sliced Blob into memory.
Voila!
But! How can we keep transactions alive when calling this non-indexedDB async call?
I use two different solutions for this:
If we are in a Worker, I use
new FileReaderSync()
instead ofnew FileReader()
.If in the main thread, I use
Dexie.waitFor()
to while reading this short elapsed chunk, keeping the transaction alive still.
Ok, fine, but how do we parse the chunk then? Cannot use JSON.parse(firstPart) because it will most definitely be incomplete.
Clarinet to the rescue. This library can read JSON and callback whenever JSON tokens come in.
Writing JSON in Chunks
Writing JSON is solved more easily. As the BlobBuilder interface was deprecated from the DOM, I firstly found this task impossible. But after digging around, I found that this SHOULD be possible if browsers implement the Blob interface correctly.
Blobs can be constructed from an array of other Blobs. This is the key.
Let's say we generate 1000 Blobs of 1MB each on a device with 512 MB RAM. If the browser does its job well, it will allow the first 200 blobs or so to reside in RAM. But then, it should start putting the remanding blobs onto temporary files.
We put all these 1000 blobs into an array and generate a final Blob from that array.
And that's pretty much it.