|
| 1 | +import type { AbsolutePath, AsyncReadable } from "./types.js"; |
| 2 | + |
| 3 | +/** |
| 4 | + * Get a file handle to a file in a directory. |
| 5 | + * |
| 6 | + * @param root - A root directory from the Web File System API. |
| 7 | + * @param path - A key to a file in the root directory. |
| 8 | + * @returns A file handle to the file. |
| 9 | + */ |
| 10 | +async function resolveFileHandleForPath(root: FileSystemDirectoryHandle, path: string): Promise<FileSystemFileHandle> { |
| 11 | + const dirs = path.split("/"); |
| 12 | + const fname = dirs.pop(); |
| 13 | + if (!fname) { |
| 14 | + throw new Error("Invalid path"); |
| 15 | + } |
| 16 | + for (const dir of dirs) { |
| 17 | + root = await root.getDirectoryHandle(dir); |
| 18 | + } |
| 19 | + return root.getFileHandle(fname); |
| 20 | +} |
| 21 | + |
| 22 | +/** |
| 23 | + * A store for zarrita based on the Web File System API. |
| 24 | + * |
| 25 | + * Note: usage requires prompting the user to select a directory to grant |
| 26 | + * access to the Web File System API. |
| 27 | + * |
| 28 | + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/File_System_API} |
| 29 | + * |
| 30 | + * @example |
| 31 | + * ```js |
| 32 | + * import * as zarr from "zarrita"; |
| 33 | + * import WebFileSystemStore from "@zarrita/storage/web-fs"; |
| 34 | + * |
| 35 | + * let directoryHandle = await globalThis.showDirectoryPicker(); |
| 36 | + * let store = new WebFileSystemStore(directoryHandle); |
| 37 | + * let root = await zarr.root(store); |
| 38 | + * let arr = await zarr.open(root.resolve("/foo"), { kind: "array" }); |
| 39 | + * ``` |
| 40 | + */ |
| 41 | +class WebFileSystemStore implements AsyncReadable { |
| 42 | + #root: FileSystemDirectoryHandle; |
| 43 | + |
| 44 | + constructor(root: FileSystemDirectoryHandle) { |
| 45 | + this.#root = root; |
| 46 | + } |
| 47 | + |
| 48 | + async get(key: AbsolutePath): Promise<Uint8Array | undefined> { |
| 49 | + let fh = await resolveFileHandleForPath(this.#root, key.slice(1)).catch(() => { |
| 50 | + // TODO: better error handling |
| 51 | + // I believe a missing file will trigger an error here, which we should explicitly |
| 52 | + // catch and return `undefined` |
| 53 | + return undefined; |
| 54 | + }); |
| 55 | + if (!fh) { |
| 56 | + return undefined; |
| 57 | + }; |
| 58 | + let file = await fh.getFile(); |
| 59 | + let buffer = await file.arrayBuffer(); |
| 60 | + return new Uint8Array(buffer); |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | +export default WebFileSystemStore; |
0 commit comments