Files written to OPFS through a sync access handle in Safari are read back out with a size of zero bytes.
Run this project:
$ npx serve # or boot a server another way
$ open 'http://localhost:3000'
Logs will print out inside the HTML.
<!doctype html> | |
<html> | |
<head> | |
<title>Safari OPFS bug demo</title> | |
</head> | |
<body> | |
<pre id="log">Log: | |
</pre> | |
<script> | |
const pre = document.getElementById('log') | |
const worker = new Worker('worker.js') | |
worker.addEventListener('message', e => { pre.innerText += e.data + '\n' }) | |
worker.postMessage('start') | |
</script> | |
</body> | |
</html> |
addEventListener('message', e => { | |
if (e.data === 'start') { start() } | |
}) | |
const contents = 'Contents of the file' | |
const encoded = (new TextEncoder()).encode(contents) | |
async function start() { | |
postMessage('starting...') | |
const root = await navigator.storage.getDirectory() | |
await writeFile(root) | |
const contentsFromDisk = await readFile(root) | |
if (contents !== contentsFromDisk) { | |
postMessage('contents from disk do not match originally written contents') | |
} | |
} | |
async function writeFile(root) { | |
const fileHandle = await root.getFileHandle('test.txt', { create: true }) | |
const accessHandle = await fileHandle.createSyncAccessHandle() | |
const byteLength = accessHandle.write(encoded, { at: 0 }) | |
await accessHandle.flush() | |
postMessage(`wrote ${byteLength} bytes to file`) | |
const checkBuffer = new Uint8Array(encoded.byteLength) | |
accessHandle.read(checkBuffer, { at: 0 }) | |
const check = (new TextDecoder()).decode(checkBuffer) | |
postMessage(`checked contents written to file: '${check}'`) | |
await accessHandle.close() | |
postMessage('closed access handle') | |
} | |
async function readFile(root) { | |
const fileHandle = await root.getFileHandle('test.txt', { create: false }) | |
const file = await fileHandle.getFile() | |
postMessage(`found file - name: ${file.name}, size: ${file.size}, type: ${file.type}`) | |
const bytesFromDisk = await file.arrayBuffer() | |
const contentsFromDisk = (new TextDecoder()).decode(bytesFromDisk) | |
postMessage(`contents of file from disk: '${contentsFromDisk}'`) | |
return contentsFromDisk | |
} |