Using the approach presented here:
https://stackoverflow.com/a/65938910/3825996
I have created a webpage where you can drop a file anywhere and the content will be shown. After dropping, the handle of the file is stored in the indexedDB and when the page is refreshed, the last dropped file will be opened again and displayed.
I would expect that if I change the file and reload the page, the new content is displayed, but it is not. However, if I drag and drop the changed file, it will correctly display the changed content.
In Chrome, I also did "Empty cache and hard reload" but it still displayed the old file content.
Why does this happen? Does a file handle store the complete file? What can I do so that the changed file is reloaded after refresh?
Here is the code: (did not work as html snippet)
<html>
<head></head>
<body>
<div id="display"></div>
<script>
// stolen from https://www.npmjs.com/package/idb-keyval
function promisifyRequest(request) {
return new Promise((resolve, reject) => {
request.oncomplete = request.onsuccess = () => resolve(request.result);
request.onabort = request.onerror = () => reject(request.error);
});
}
function createStore(dbName, storeName) {
const request = indexedDB.open(dbName);
request.onupgradeneeded = () => request.result.createObjectStore(storeName);
const dbp = promisifyRequest(request);
return (txMode, callback) => dbp.then((db) => callback(db.transaction(storeName, txMode).objectStore(storeName)));
}
let defaultGetStoreFunc;
function defaultGetStore() {
if (!defaultGetStoreFunc) {
defaultGetStoreFunc = createStore('keyval-store', 'keyval');
}
return defaultGetStoreFunc;
}
function idbLoad(key, customStore = defaultGetStore()) {
return customStore('readonly', (store) => promisifyRequest(store.get(key)));
}
function idbStore(key, value, customStore = defaultGetStore()) {
return customStore('readwrite', (store) => {
store.put(value, key);
return promisifyRequest(store.transaction);
});
}
function makeKey(domElement) {
return 'lastOpened-' + domElement.tagName + "-" + domElement.id;
}
async function handleFile(domElement, file) {
await idbStore(makeKey(domElement), file);
const reader = new FileReader();
reader.onload = function (e) {
domElement.dispatchEvent(
new CustomEvent("handlefilecontent", { detail: e.target.result })
);
};
reader.readAsText(file);
}
function dropHandler(ev) {
ev.preventDefault();
if (ev.dataTransfer.items) {
[...ev.dataTransfer.items].forEach((item, i) => {
if (item.kind === "file") {
const file = item.getAsFile();
handleFile(ev.currentTarget, file);
}
});
} else {
[...ev.dataTransfer.files].forEach((file, i) => {
handleFile(ev.currentTarget, file);
});
}
}
function dragOverHandler(ev) {
ev.preventDefault();
}
async function checkLastOpened(domElement) {
try {
let file = await idbLoad(makeKey(domElement));
if (file) {
handleFile(domElement, file)
}
} catch (error) {
alert(error.name, error.message);
}
}
function dragDropInitialize(domElement, callback) {
domElement.addEventListener("drop", dropHandler)
domElement.addEventListener("dragover", dragOverHandler)
domElement.addEventListener("handlefilecontent", (ev) => { callback(ev.detail) })
checkLastOpened(domElement)
}
dragDropInitialize(document.body, (content) => {
document.getElementById("display").innerHTML = content
})
</script>
</body>
</html>
The objects returned via DataTransferItem
's getAsFile()
or DataTransfer
's files
list are File
objects, which are not equivalent to a file handle but represent the data of the file itself - a File
in the web platform API is basically just a Blob
with a name.
So when you call idbStore()
passing the file, you're actually storing the contents of the file to the database. At the point when the store happens, implementations will slurp in the contents of the file and store it in the database. This is why you're not seeing new data when you reload the page.