Read, write, and organize files in the app's own folders on iOS, Android, and web
One.FileSystem works with files in the app’s private folders: documents,
caches, application support, and a temporary folder. On iOS it is
FileManager; on Android it is java.io.File under the app’s data folder;
on web it is the browser’s Origin Private File System.
It has two layers. Flat promise functions such as writeFile and
readDirectory cover everyday work and run on every platform. The File,
Directory, and Paths classes add typed reads, synchronous calls, hashes,
random access handles, and streams on iOS and Android.
import { One } from 'one'
const { File, Directory, Paths } = One.FileSystem
async function saveDraft(id: string, body: string) { const drafts = new Directory(Paths.document, 'drafts') if (!drafts.exists) drafts.create() await new File(drafts, `${id}.txt`).write(body)}
async function readDraft(id: string) { const file = new File(Paths.document, 'drafts', `${id}.txt`) return file.exists ? file.text() : null}A File or Directory is only a location until you act on it, so building
one never touches the disk.
Locations. Every path is an absolute file:// URI. getDirectories()
returns the four app folders, each ending in /. On Android they are a
Documents folder inside the app’s files folder, the cache folder, the files
folder, and a tmp folder inside the cache. Join names onto them with the
class constructors or new URL(name, folder).
Threads. Async calls run on one serial background queue per app: a
dispatch queue on iOS and a single-thread executor on Android. Because the
queue is serial, calls you await in order see each other’s results. Sync
calls, such as textSync, writeSync, list, and create, run on the
JavaScript thread and block it until the disk answers.
Protected roots. The app’s home, Documents, Library, Caches, Application
Support, and temporary folders cannot be moved or deleted; trying rejects
with E_FILE_PERMISSION. Their contents can.
Reading bytes. Besides text(), bytes(), and base64(), One’s
fetch on iOS and Android reads file:// URIs, including files that the
document and image pickers return.
Web. Files live in the Origin Private File System, private to the site
and invisible to people. URIs look like opfs://one/documents/. Every
operation runs in order, and a write resolves only once its stream closes.
Only the async members work there; the sync ones throw.
Flat functions
| Function | Resolves |
|---|---|
| getDirectories() | The four folders, at once |
| getInfo(uri) | What is at the URI |
| readDirectory(uri) | The entries in a folder |
| makeDirectory(uri, intermediates?) | Once created |
| writeFile(uri, contents, encoding?) | Once written |
| copy(from, to), move(from, to) | Once done |
| delete(uri) | Once removed, recursively |
| FileInfo | Type | Description |
|---|---|---|
uristringThe URI asked about. | string | The URI asked about. |
existsbooleanWhether anything is there. | boolean | Whether anything is there. |
isDirectorybooleanWhether it is a folder. | boolean | Whether it is a folder. |
sizenumberSize in bytes. | number | Size in bytes. |
modifiedAtnumberLast change, in Unix milliseconds. | number | Last change, in Unix milliseconds. |
| FileEntry | Type | Description |
|---|---|---|
namestringThe file or folder name. | string | The file or folder name. |
uristringIts full URI. | string | Its full URI. |
isDirectorybooleanWhether it is a folder. | boolean | Whether it is a folder. |
File
| Member | Does |
|---|---|
| uri, name, extension | Read the location |
| exists, size, type | Read live metadata |
| creationTime, lastModified | Times, or null |
| text, bytes, base64 | Read, with Sync twins |
| arrayBuffer, json | Read as a buffer or JSON |
| write(content, options?) | Write, with a Sync twin |
| digest(algorithm) | A lowercase hex hash |
| create, delete, rename | Change it, synchronously |
| copy, move | Relocate, with Sync twins |
| open(mode?) | A FileHandle |
| readableStream, writableStream | Streams of bytes |
Directory
| Member | Does |
|---|---|
| uri, name, exists, size | Read the location and size |
| list() | Sorted File and Directory children |
| create(options?) | Create it |
| createFile, createDirectory | Create and return a child |
| delete, rename | Change it |
| copy, move | Relocate, with Sync twins |
Paths has document, cache, and bundle folders, totalDiskSpace
and availableDiskSpace in bytes, info(...uris), and string helpers that
take files, folders, or strings: join, dirname, basename, extname,
normalize, parse, relative, and isAbsolute.
| FileWriteOptions | Type | Description |
|---|---|---|
encodingFileEncoding = "utf8"utf8 or base64, for string content. | FileEncoding = "utf8" | utf8 or base64, for string content. |
appendboolean = falseAdd to the end instead of replacing. | boolean = false | Add to the end instead of replacing. |
| DirectoryCreateOptions | Type | Description |
|---|---|---|
intermediatesbooleanCreate missing parent folders. | boolean | Create missing parent folders. |
overwritebooleanReplace what is already there. | boolean | Replace what is already there. |
idempotentbooleanSucceed if the folder exists. | boolean | Succeed if the folder exists. |
FileMode is ReadWrite (the default), ReadOnly, WriteOnly, Append,
or Truncate; write-only modes create a missing file. digest takes
'MD5', 'SHA-1', 'SHA-256', 'SHA-384', or 'SHA-512'.
Failures reject with an error code: E_FILE_URI, E_FILE_ENCODING,
E_FILE_NOT_FOUND, E_FILE_EXISTS, E_FILE_PERMISSION, or E_FILE_FAILED.
| iOS | Android | Web | |
|---|---|---|---|
| Storage | App sandbox | App data folder | Private browser storage |
| Flat functions | Yes | Yes | Yes |
| Sync members | Yes | Yes | Throw |
| App bundle | Readable | Rejects | |
| MD5 digest | Yes | Yes | Rejects |
| Errors | Error codes | Error codes | Browser error names |
writeFile and write fail when the
folder is missing. Create it first; makeDirectory creates parents by
default, and the class create takes intermediates.{ overwrite: true }.E_FILE_ENCODING.fetch and image elements cannot read
opfs:// URIs. Use the browser’s navigator.storage.getDirectory() file
handles for that, and expect quota or privacy settings to reject writes.import { One } from 'one'
const { File, Paths } = One.FileSystem
async function cachedImage(url: string, name: string) { const file = new File(Paths.cache, name) if (file.exists) return file.uri const response = await fetch(url) await file.write(new Uint8Array(await response.arrayBuffer())) return file.uri}The returned URI works as an image source. The cache folder suits it: the system can reclaim the space, and the next call downloads it again.
import { One } from 'one'
const { File, Directory, Paths } = One.FileSystem
async function importFile(pickedUri: string) { const inbox = new Directory(Paths.document, 'inbox') inbox.create({ intermediates: true, idempotent: true }) const source = new File(pickedUri) await source.copy(inbox, { overwrite: true }) return new File(inbox, source.name)}Copying into a directory keeps the source’s name. idempotent lets the
folder already exist, so the function is safe to call again.
import { One } from 'one'
async function countBytes(uri: string) { const reader = new One.FileSystem.File(uri).readableStream().getReader() let total = 0 for (;;) { const { done, value } = await reader.read() if (done) return total total += value.length }}The stream reads 1024 bytes at a time and closes its handle when it ends,
so a large file never sits in memory whole. For a hash of the whole file,
digest('SHA-256') does the reading for you.
import { One } from 'one'
async function saveExport(json: string) { const { documents } = One.FileSystem.getDirectories() const folder = new URL('exports/', documents).href await One.FileSystem.makeDirectory(folder) await One.FileSystem.writeFile(new URL('latest.json', folder).href, json)}The flat functions are the part that also runs on web, where documents is
an opfs:// folder, so shared code that must work in the browser uses them.
The path and stream helpers are adapted from Expo FileSystem and Node.js.
Edit this page on GitHub.