Skip to content

FileStorage API

Write Files

Writing file using the write method:

try {
await storage.write('path/to/file.txt', contents);
} catch (err) {
if (err instanceof UnableToWriteFile) {
// handle error
}
}

The write method accepts a string, Uint8Array (or Buffer), or any Readable stream. When writing a file, any of the parent directories are automatically created if the underlying storage implementation requires directories to exist.

You can specify visibility of a file when writing it:

import {VISIBILITY} from '@flystorage/file-storage';
await storage.write('path/to/file.txt', contents, {
visibility: Visibility.PUBLIC,
});

Read Files

Read a file using the read, readToString, readToBuffer, or readToUint8Array methods:

try {
/**
* @type {Readable}
*/
const contents = await storage.read('path/to/file.txt');
} catch (err) {
if (err instanceof UnableToReadFile) {
// handle error
}
}

Delete Files

Delete a file using the delete method. Deleting files deletes files if they exist. To simplify common scenarios, there is no error when you try to delete a non-existing file.

try {
await storage.deleteFile('path/to/file.txt');
} catch (err) {
if (err instanceof UnableToDeleteFile) {
// handle error
}
}

Create Directories

The createDirectory method allows you to explicitly create directories. Some implementations do not support the creation of actual or virtual directories, in those cases this method is a no-op.

try {
await storage.createDirectory('path/to/directory');
} catch (err) {
if (err instanceof UnableToCreateDirectory) {
// handle error
}
}

In cases the filesystem requires nested directories, all encapsulated directories are implicitly created.


Delete Directories

The deleteDirectory method allows you to delete a directory. Any contents of the directory is implicitly deleted as well. Only use this method if you want to delete everything contained in the directory.

try {
await storage.deleteDirectory('path/to/directory');
} catch (err) {
if (err instanceof UnableToDeleteDirectory) {
// handle error
}
}

For storage implementation that do not support directories, these implementations emulate the deletion of anything that matches the directory prefix. This produces consistent behaviour across adapters.


File or directory info

Use the stat or statFile methods to retrieve stat information for a file or directory.

try {
const stat = await storage.stat('path/to/file.txt');
} catch (err) {
if (err instanceof UnableToGetStat) {
// handle error
}
}

The stat method has normalised behaviour across implementation for files. Some implementations do not support directories, in which case you’ll get an error. The statFile method ensures the returned stat entry is a FileInfo and fails if the returned stat entry is a DirectoryInfo.


Moving Files

Files can be moved using the moveFile method. The visibility is a file is retained, unless the retainVisibility option is set to false. When set to false the default visibility is used. You can also explicitly set the visibility option to set it, which prevents a call to resolve it from the existing file. This may be beneficial for performance.

try {
await storage.moveFile('from/here.txt', 'to/there.txt', {
visibility: Visibility.PRIVATE,
});
} catch (err) {
if (err instanceof UnableToMoveFile) {
// handle error
}
}

Moving files works reliably across implementations. Some implementation support moving of directories this behaviour is not normalised across implementations.


Copying Files

Files can be copied using the copyFile method. The visibility is a file is retained, unless the retainVisibility option is set to false. When set to false the default visibility is used. You can also explicitly set the visibility option to set it, which prevents a call to resolve it from the existing file. This may be beneficial for performance.

try {
await storage.copyFile('from/here.txt', 'to/there.txt', {
visibility: Visibility.PRIVATE,
});
} catch (err) {
if (err instanceof UnableToCopyFile) {
// handle error
}
}

Change visibility

The visibility of files can be changed using the changeVisibility method. To understand visibility, read up on the visibility documentation.

try {
await storage.changeVisibility('path/to/file.txt');
} catch (err) {
if (err instanceof UnableToCopyFile) {
// handle error
}
}

Some implementations, like Azure Storage Blob, do not support visibility and may throw an error.


Determine visibility

The visibility of files can be resolved using the visibility method. To understand visibility, read up on the visibility documentation.

try {
const visibility = await storage.visibility('path/to/file.txt');
} catch (err) {
if (err instanceof UnableToGetVisibility) {
// handle error
}
}

Some implementations, like Azure Storage Blob, do not support visibility and may throw an error.


File Existence

You can check if a file exists using the fileExists method.

try {
const exists = await storage.fileExists('path/to/file.txt');
} catch (err) {
if (err instanceof UnableToCheckFileExistence) {
// handle error
}
}

Directory Existence

You can check if a directory exists using the directoryExists method.

try {
const exists = await storage.directoryExists('path/to/directory');
} catch (err) {
if (err instanceof UnableToCheckDirectoryExistence) {
// handle error
}
}

When the underlying implementation does not support actual directories, the behaviour is emulated by using the least expensive way to list files that match the directory prefix.


List Directory Contents

The contents is a directory can be listed using the list method. Recursive or deep listings can be fetched by setting the deep option to true.

try {
const listing = storage.list('path/to/directory', {deep: true});
} catch (err) {
if (err instanceof UnableToCheckDirectoryExistence) {
// handle error
}
}

Looping over listings

The returned directory listing is an AsyncIterable and can be used to loop over the contents.

for await (const entry of listing) {
if (entry.type === 'file' || entry.isFile) {
// handle the file
}
if (entry.type === 'directory' || entry.isDirectory) {
// handle a directory
}
}

You can check the type of the entry using the type property (file or directory) or by using the isFile and/or isDirectory properties.

Collect a listing as an array

Listings come with convenience method to make handling them easier. The toArray method collects the entries and returns them as an array.

const listingAsArray = await listing.toArray();

try {
const exists = await storage.directoryExists('path/to/directory');
} catch (err) {
if (err instanceof UnableToCheckDirectoryExistence) {
// handle error
}
}

When the underlying implementation does not support actual directories, the behaviour is emulated by using the least expensive way to list files that match the directory prefix.


Public URLs

The publicUrl method resolves the public URL of a file.

try {
const url = await storage.publicUrl('path/to/file.txt');
} catch (err) {
if (err instanceof UnableToGetPublicUrl) {
// handle error
}
}

Implementation that do not support public URLs may throw an error. Some adapter may require you to inject a public url resolving strategy to enable them to produce public URLs.


Temporary URLs

The temporaryUrl method resolves a temporary URL to a file. The second argument accepts either a Date or a number (millisecond precise timestamp) to indicate when the link should expire.

try {
const expiresAt = Date.now() + 24 * 60 * 1000;
const url = await storage.temporaryUrl('path/to/file.txt', expiresAt);
} catch (err) {
if (err instanceof UnableToGetTemporaryUrl) {
// handle error
}
}

Implementation that do not support temporary URLs may throw an error. Some adapter may require you to inject a temporary url resolving strategy to enable them to produce temporary URLs.


Last Modified

The lastModified method resolves the last modification time of a file.

try {
const timestamp = await storage.lastModified('path/to/file.txt');
} catch (err) {
if (err instanceof UnableToGetLastModified) {
// handle error
}
}

Mime-type

The mimeType method resolves the mime-type of a file.

try {
const mimetype = await storage.mimeType('path/to/file.txt');
} catch (err) {
if (err instanceof UnableToGetMimeType) {
// handle error
}
}

File Size

The fileSize method resolves the size of a file.

try {
const size = await storage.fileSize('path/to/file.txt');
} catch (err) {
if (err instanceof UnableToGetFileSize) {
// handle error
}
}

Checksums

The checksum method resolves the hash/checksum/etag of a file. Optionally, an algo option can be passed if a specific type of checksum should be retrieved.

try {
const checksum = await storage.checksum('path/to/file.txt', optionalOptions);
} catch (err) {
if (err instanceof UnableToGetChecksum) {
// handle error
}
}

When the underlying storage solution does not expose pre-computed checksums, a checksum is calculated in-memory. This involves streaming the entire file, which may be a costly operation.

When checksums are calculated, the resulting hash is encoded. By default hex is used, you can specify any suitable BinaryToTextEncoding value using the encoding option.


try {
await storage.deleteFile('path/to/file.txt');
} catch (err) {
if (err instanceof UnableToDeleteFile) {
// handle error
}
}