162 lines
5.7 KiB
HTML
162 lines
5.7 KiB
HTML
|
<html>
|
||
|
<head>
|
||
|
<meta charset="utf-8">
|
||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
|
</head>
|
||
|
<body>
|
||
|
<ul id="files"></ul>
|
||
|
<p>
|
||
|
<input type="file" id="file" multiple capture="user" accept="image/*">
|
||
|
<button id="addFile">Add</button>
|
||
|
<button id="drop">Delete all</button>
|
||
|
</p>
|
||
|
<script type="text/javascript">
|
||
|
|
||
|
|
||
|
function reqAsPromise(req) {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
req.onsuccess = () => resolve(req);
|
||
|
req.onerror = (err) => reject(err);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function fetchResults(cursor, isDone, resultMapper) {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
const results = [];
|
||
|
cursor.onerror = (event) => {
|
||
|
reject(new Error("Query failed: " + event.target.errorCode));
|
||
|
};
|
||
|
// collect results
|
||
|
cursor.onsuccess = (event) => {
|
||
|
const cursor = event.target.result;
|
||
|
if (!cursor) {
|
||
|
resolve(results);
|
||
|
return; // end of results
|
||
|
}
|
||
|
results.push(resultMapper(cursor));
|
||
|
if (!isDone(results)) {
|
||
|
cursor.continue();
|
||
|
} else {
|
||
|
resolve(results);
|
||
|
}
|
||
|
};
|
||
|
});
|
||
|
}
|
||
|
|
||
|
class Storage {
|
||
|
constructor(databaseName) {
|
||
|
this._databaseName = databaseName;
|
||
|
this._database = null;
|
||
|
}
|
||
|
|
||
|
async open() {
|
||
|
const req = window.indexedDB.open(this._databaseName);
|
||
|
req.onupgradeneeded = (ev) => {
|
||
|
const db = ev.target.result;
|
||
|
const oldVersion = ev.oldVersion;
|
||
|
this._createStores(db, oldVersion);
|
||
|
};
|
||
|
await reqAsPromise(req);
|
||
|
this._database = req.result;
|
||
|
}
|
||
|
|
||
|
async drop() {
|
||
|
if (this._database) {
|
||
|
this._database.close();
|
||
|
this._database = null;
|
||
|
}
|
||
|
await reqAsPromise(window.indexedDB.deleteDatabase(this._databaseName));
|
||
|
}
|
||
|
|
||
|
_createStores(db) {
|
||
|
db.createObjectStore("files", {keyPath: "id"});
|
||
|
}
|
||
|
|
||
|
async storeFile(file) {
|
||
|
const id = Math.floor(Math.random() * 10000000000);
|
||
|
console.log(`adding a file as id ${id}`);
|
||
|
const tx = this._database.transaction(["files"], "readwrite");
|
||
|
const store = tx.objectStore("files");
|
||
|
await reqAsPromise(store.add({id, file}));
|
||
|
}
|
||
|
|
||
|
|
||
|
getFiles() {
|
||
|
const tx = this._database.transaction(["files"], "readonly");
|
||
|
const store = tx.objectStore("files");
|
||
|
const cursor = store.openCursor();
|
||
|
return fetchResults(cursor,
|
||
|
() => false,
|
||
|
(cursor) => cursor.value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async function reloadFiles(storage, fileList) {
|
||
|
const files = await storage.getFiles();
|
||
|
const fileNodes = files.map(f => {
|
||
|
const {type, size, name} = f.file;
|
||
|
const txt = document.createTextNode(`${f.id} - ${name} of type ${type} - size: ${Math.round(size / 1024, 2)}kb`);
|
||
|
const li = document.createElement("li");
|
||
|
li.addEventListener("click", async () => {
|
||
|
const reader = new FileReader();
|
||
|
const promise = new Promise((resolve, reject) => {
|
||
|
reader.onload = e => resolve(e.target.result);
|
||
|
reader.onerror = e => reject(e.target.error);
|
||
|
});
|
||
|
reader.readAsArrayBuffer(f.file);
|
||
|
try {
|
||
|
const buf = await promise;
|
||
|
alert(`read blob, len is ${buf.byteLength}`);
|
||
|
} catch(e) {
|
||
|
alert(e.message);
|
||
|
}
|
||
|
});
|
||
|
li.appendChild(txt);
|
||
|
return li;
|
||
|
});
|
||
|
fileList.innerHTML = "";
|
||
|
for(const li of fileNodes) {
|
||
|
fileList.appendChild(li);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async function main() {
|
||
|
let storage = new Storage("idb-store-files-test");
|
||
|
await storage.open();
|
||
|
|
||
|
const fileList = document.getElementById("files");
|
||
|
const dropButton = document.getElementById("drop");
|
||
|
const addButton = document.getElementById("addFile");
|
||
|
const filePicker = document.getElementById("file");
|
||
|
addButton.addEventListener("click", async () => {
|
||
|
const files = Array.from(filePicker.files);
|
||
|
try {
|
||
|
for(const file of files) {
|
||
|
await storage.storeFile(file);
|
||
|
}
|
||
|
alert(`stored ${files.length} files!`);
|
||
|
reloadFiles(storage, fileList);
|
||
|
} catch(e) {
|
||
|
alert(e.message);
|
||
|
}
|
||
|
});
|
||
|
dropButton.addEventListener("click", async () => {
|
||
|
try {
|
||
|
if (storage) {
|
||
|
await storage.drop();
|
||
|
storage = null;
|
||
|
alert("dropped db");
|
||
|
}
|
||
|
} catch(e) {
|
||
|
alert(e.message);
|
||
|
}
|
||
|
});
|
||
|
reloadFiles(storage, fileList);
|
||
|
}
|
||
|
|
||
|
main();
|
||
|
|
||
|
</script>
|
||
|
</body>
|
||
|
</html>
|