expo-file-system
Version:
Provides access to the local file system on the device.
176 lines (150 loc) • 4.94 kB
text/typescript
import { ReadableStream, WritableStream } from 'web-streams-polyfill';
import ExpoFileSystem from './ExpoFileSystem';
import { PathUtilities } from './pathUtilities';
import { FileSystemReadableStreamSource, FileSystemWritableSink } from './streams';
export class Paths extends PathUtilities {
/**
* A property containing the cache directory – a place to store files that can be deleted by the system when the device runs low on storage.
*/
static get cache() {
return new Directory(ExpoFileSystem.cacheDirectory);
}
/**
* A property containing the document directory – a place to store files that are safe from being deleted by the system.
*/
static get document() {
return new Directory(ExpoFileSystem.documentDirectory);
}
static get appleSharedContainers() {
const containers: Record<string, string> = ExpoFileSystem.appleSharedContainers ?? {};
const result: Record<string, Directory> = {};
for (const appGroupId in containers) {
result[appGroupId] = new Directory(containers[appGroupId]);
}
return result;
}
}
export class FileBlob extends Blob {
file: File;
key: string = 'FileBlob';
constructor(file: File) {
super();
this.file = file;
}
get size(): number {
return this.file.size ?? 0;
}
get name(): string {
return this.file.name;
}
get type(): string {
return this.file.type ?? '';
}
async arrayBuffer(): Promise<ArrayBuffer> {
return this.file.bytes().buffer as ArrayBuffer;
}
async text(): Promise<string> {
return this.file.text();
}
async bytes(): Promise<Uint8Array> {
return this.file.bytes();
}
stream(): ReadableStream<Uint8Array> {
return this.file.readableStream();
}
slice(start?: number, end?: number, contentType?: string): Blob {
return new Blob([this.file.bytes().slice(start, end)], { type: contentType });
}
}
export class File extends ExpoFileSystem.FileSystemFile {
/**
* Creates an instance of a file.
* @param uris - An array of: `file:///` string URIs, `File` instances, `Directory` instances representing an arbitrary location on the file system. The location does not need to exist, or it may already contain a directory.
* @example
* ```ts
* const file = new File("file:///path/to/file.txt");
* ```
*/
constructor(...uris: (string | File | Directory)[]) {
super(Paths.join(...uris));
this.validatePath();
}
/*
* Returns the file as a Blob. The blob can be used in `@expo/fetch` to send files over network and for other uses.
*/
blob(): Blob {
return new FileBlob(this);
}
/*
* Directory containing the file.
*/
get parentDirectory() {
return new Directory(Paths.dirname(this.uri));
}
/**
* File extension.
* @example '.png'
*/
get extension() {
return Paths.extname(this.uri);
}
/**
* File name. Includes the extension.
*/
get name() {
return Paths.basename(this.uri);
}
readableStream() {
return new ReadableStream<Uint8Array>(new FileSystemReadableStreamSource(super.open()));
}
writableStream() {
return new WritableStream<Uint8Array>(new FileSystemWritableSink(super.open()));
}
}
// Cannot use `static` keyword in class declaration because of a runtime error.
File.downloadFileAsync = async function downloadFileAsync(url: string, to: File | Directory) {
const outputPath = await ExpoFileSystem.downloadFileAsync(url, to);
return new File(outputPath);
};
/**
* Represents a directory on the filesystem.
*
* A `Directory` instance can be created for any path, and does not need to exist on the filesystem during creation.
*/
export class Directory extends ExpoFileSystem.FileSystemDirectory {
/**
* Creates an instance of a directory.
* @param uris - An array of: `file:///` string URIs, `File` instances, `Directory` instances representing an arbitrary location on the file system. The location does not need to exist, or it may already contain a file.
* @example
* ```ts
* const directory = new Directory("file:///path/to/directory");
* ```
*/
constructor(...uris: (string | File | Directory)[]) {
super(Paths.join(...uris));
this.validatePath();
}
/*
* Directory containing the file.
*/
get parentDirectory() {
return new Directory(Paths.join(this.uri, '..'));
}
/**
* Lists the contents of a directory.
* Calling this method if the parent directory does not exist will throw an error.
* @returns An array of `Directory` and `File` instances.
*/
list(): (Directory | File)[] {
// We need to wrap it in the JS File/Directory classes, and returning SharedObjects in lists is not supported yet on Android.
return super
.listAsRecords()
.map(({ isDirectory, path }) => (isDirectory ? new Directory(path) : new File(path)));
}
/**
* Directory name.
*/
get name() {
return Paths.basename(this.uri);
}
}