JSFiddle - React, Tailwind, and code Playground

by Sebastian Kay

HTML

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
<div class="container mt-4 mb-4">
<div class="row mb-4">
    <div class="col"></div>
    <div class="col-12 col-lg-6">
      <div class="card">
        <div class="card-body">
          <h2 class="h5 card-title">Indexed DB Demo</h2>
          <p>This demo shows how to use IndexedDB to store files in the browser and make them persistent. Please note that the way IndexedBD is implemented highly depends on the browser you're using.
          </p>
          <blockquote class="alert alert-info">
            <p>This browser has a storage quota of <b id="storage-total">0</b> <br>It currently uses <b id="storage-used">0</b> <br> and has around <b id="storage-free">0</b> free </p>
            <p>Note: These values are estimates calculated with Javascript.</p>
          </blockquote>

          <form id="file-form">
            <label for="file" class="form-label">Select a file</label>
            <input type="file" id="file" class="form-control mb-2">
            <div class="d-grid gap-2">
              <input type="submit" value="Add to IndexedDB" class="btn btn-primary block">
              <input type="button" value="Clear" id="clear-button" class="btn btn-danger block">
            </div>
          </form>
        </div>
      </div>
    </div>
    <div class="col"></div>
  </div>
  <div class="row">
    <div class="col-12">
      <h2>Image Gallery</h2>
      <p>You can add images to the gallery by selecting a file and clicking the "Add to IndexedDB" button. All of your files will be stored locally in your browser.</p>
    </div>
  </div>
  <div class="row" id="images"></div>
 </div>

JavaScript

'use strict';

const storeName = 'localFiles';
const storeKey = 'fileName';
const dbVersion = 1;
let db = null;

// IndexedDB Methods
const initIndexedDb = (dbName, stores) => {
	return new Promise((resolve, reject) => {
		const request = indexedDB.open(dbName, dbVersion);
		request.onerror = (event) => {
			reject(event.target.error);
		};
		request.onsuccess = (event) => {
			resolve(event.target.result);
		};
		request.onupgradeneeded = (event) => {
			stores.forEach((store) => {
				const objectStore = event.target.result.createObjectStore(store.name, {
					keyPath: store.keyPath,
				});
				objectStore.createIndex(store.keyPath, store.keyPath, { unique: true });
			});
		};
	});
};

const clearEntriesFromIndexedDb = () => {
	const store = db.transaction(storeName, 'readwrite').objectStore(storeName);

	store.clear();
	clearGalleryImages();

	store.transaction.oncomplete = () => {
		renderStorageQuotaInfo();
	};
};

const deleteImageFromIndexedDb = (storeKey) => {
	const store = db.transaction(storeName, 'readwrite').objectStore(storeName);
	store.delete(storeKey);
	store.transaction.oncomplete = async () => {
		console.log('Deleting image from DB' + storeKey)
		clearGalleryImages();
		renderAvailableImagesFromDb();
		await renderStorageQuotaInfo();
	};
};

const renderAvailableImagesFromDb = () => {
	db.transaction(storeName, 'readonly').objectStore(storeName).openCursor().onsuccess = (event) => {
		const cursor = event.target.result;
		if (cursor) {
			renderGalleryColumn(cursor);
			cursor.continue();
		}
	};
};

// Form functions
/**
 * @desc Gets the file from the input field and adds it to the IndexedDB
 * @param {Event} ev
 * @returns {Promise<void>}
 */
const handleSubmit = async (ev) => {
	ev.preventDefault();
	const file = await getFileFromInput();
	const store = db.transaction(storeName, 'readwrite').objectStore(storeName);
	store.add(file);

	store.transaction.oncomplete = () =>...