indexeddb-fs
An fs library for the browser that lets you store data using an API similar to Node's fs module. It's powered by IndexedDB, a browser database.
Motivation
Other solutions I found didn't work well. They lacked validation and didn't create directories properly.
Installation
npm install indexeddb-fs
A simple example of how you can use indexeddb-fs
import fs from 'indexeddb-fs';
async function main() {
const directoryExists = await fs.isDirectory('my_directory');
if (!directoryExists) {
await fs.createDirectory('my_directory');
}
const content = 'Hello, world!';
await fs.writeFile('my_directory/my_file.txt', content);
const readContent = await fs.readFile('my_directory/my_file.txt');
console.log(readContent);
await fs.removeDirectory('my_directory');
}
main();
A more complex example that demonstrates the use of all the functions
import fs from 'indexeddb-fs';
async function main() {
await fs.createDirectory('my_directory');
await fs.createDirectory('my_directory/subdirectory');
await fs.createDirectory('my_directory/another_subdirectory');
const content = 'Hello, world!';
await fs.writeFile('my_directory/my_file.txt', content);
const fileExists = await fs.exists('my_directory/my_file.txt');
const isAFile = await fs.isFile('my_directory/my_file.txt');
console.log('File exists:', fileExists);
console.log('Is a file:', isAFile);
await fs.copyFile('my_directory/my_file.txt', 'my_directory/another_subdirectory/my_file_copy.txt');
await fs.renameFile('my_directory/my_file.txt', 'my_directory/new_file_name.txt');
await fs.moveFile('my_directory/new_file_name.txt', 'my_directory/subdirectory/new_location.txt');
const { filesCount, directoriesCount } = await fs.readDirectory('my_directory');
console.log('Number of files:', filesCount);
console.log('Number of subdirectories:', directoriesCount);
await fs.removeDirectory('my_directory');
const copiedFileContent = await fs.readFile('my_directory/another_subdirectory/my_file_copy.txt');
console.log('Copied file content:', copiedFileContent);
}
main();
Custom fs object
import { createFs } from 'indexeddb-fs';
const fs = createFs({
databaseVersion: 'indexeddb version (default "1")',
objectStoreName: 'store name in indexeddb (default "files")',
rootDirectoryName: 'your root directory name (default "root") ',
databaseName: 'indexeddb database name (default "indexeddb-fs")',
});
Api
- All methods in indexeddb-fs return a Promise since IndexedDB is an asynchronous API.
- Each method can reject the returned Promise with an
Error
object when an error occurs. - It's important to handle errors appropriately in your code to ensure your application is robust and doesn't break unexpectedly.
Fields
The indexeddb-fs library contains several configuration fields that can be accessed after importing the library. These fields include:
databaseName
: the name of the IndexedDB database used by the library.databaseVersion
: the version number of the IndexedDB database used by the library.objectStoreName
: the name of the object store used by the library to store data.rootDirectoryName
: the name of the root directory used by the library.
To access these fields, import them from the indexeddb-fs
module as shown in the example code above. These fields contain configuration information for the library and can also be used to access default values.
Common functions
fs.exists(fullPath)
- Parameters: [
fullPath
: string] - Returns:
Promise<boolean>
- Description: Returns a promise that resolves to
true
if the file or directory at the given fullPath
exists, and false
otherwise.
Example of usage:
const fileExists = await fs.exists('path/to/file.txt');
if (fileExists) {
console.log('The file exists!');
} else {
console.log('The file does not exist.');
}
const dirExists = await fs.exists('path/to/directory');
if (dirExists) {
console.log('The directory exists!');
} else {
console.log('The directory does not exist.');
}
fs.remove(fullPath)
- Parameters: [
fullPath
: string] - Returns:
Promise<void>
- Description: Removes the file or directory at the given
fullPath
. The method does not remove directories recursively, so it will throw an error if the path is not empty.
Example of usage:
await fs.writeFile('file1.txt', 'test content');
await fs.remove('file1.txt');
await fs.exists('file1.txt').then((result) => {
console.log(!result);
});
await fs.createDirectory('directory1');
await fs.remove('directory1');
await fs.exists('directory1').then((result) => {
console.log(!result);
});
await fs.createDirectory('directory2');
await fs.writeFile('directory2/file2.txt', 'test content');
await fs.remove('directory2').catch((error) => {
console.error(error.message);
});
await fs.exists('directory2').then((result) => {
console.log(result);
});
fs.details(fullPath)
- Parameters: [
fullPath
: string] - Returns:
Promise<FileEntry<any> | DirectoryEntry>
- Description: Returns an object containing details about the file or directory at the given
fullPath
. The object contains the following properties:
type
: The type of the entry (file or directory).name
: The name of the entry.directory
: The name of the directory that contains the entry.fullPath
: The full path of the entry, including the directory.
- Throws an error when the path does not contain anything.
Example of usage:
const createdDirectory = await fs.createDirectory('directory');
const createdDirectoryDetails = await fs.details(createdDirectory.fullPath);
console.log('Type:', createdDirectoryDetails.type);
console.log('Name:', createdDirectoryDetails.name);
console.log('Directory:', createdDirectoryDetails.directory);
console.log('Full Path:', createdDirectoryDetails.fullPath);
Example result for FileEntry<any>
type:
{
type: 'file',
name: 'file.txt',
directory: 'root',
data: 'test 2 content',
createdAt: 1626882161631,
fullPath: 'root/file.txt'
}
Example result for DirectoryEntry
type:
{
isRoot: false,
directory: 'root',
type: 'directory',
name: 'directory',
createdAt: 1626882291087,
fullPath: 'root/directory'
}
File functions
fs.isFile(fullPath)
- Parameters: [
fullPath
: string] - Returns:
Promise<boolean>
- Description: Returns a promise that resolves to
true
if a file exists at the given fullPath
, and false
otherwise. If the path does not contain anything, an error is thrown.
Example of usage:
import fs from 'indexeddb-fs';
await fs.createDirectory('files');
await fs.createDirectory('directories');
await fs.isFile('files').then((result) => {
console.log(result);
});
await fs.isFile('directories').then((result) => {
console.log(result);
});
await fs.writeFile('file', 'content');
await fs.isFile('file').then((result) => {
console.log(result);
});
await fs.writeFile('files/file', 'content');
await fs.isFile('files/file').then((result) => {
console.log(result);
});
Example result for FileEntry<TData>
type:
{
type: 'file',
name: 'file.txt',
directory: 'root',
data: 'test 2 content',
createdAt: 1626882161631,
fullPath: 'root/file.txt'
}
fs.writeFile(fullPath, data)
- Parameters: [
fullPath
: string, data
: TData] - Returns:
Promise<FileEntry<TData>>
- Description: Writes
data
to the file specified by fullPath
, replacing the file if it already exists. The data
parameter can be any data you want to write to the file, and is usually a string or an object that can be serialized to JSON. The method returns a promise that resolves to a FileEntry
object representing the file that was written. - Throws an error when the destination directory of the file does not exist. This can happen if the path to the file contains one or more directories that do not exist yet. You can use the
createDirectory
method to create the missing directories. - Throws an error when the path contains a directory with the same name as the file. For example, if a directory named "file.txt" exists, you cannot create a file with the same name in the same directory.
Example of usage:
import fs from 'indexeddb-fs';
await fs.createDirectory('my_directory');
const fileEntry = await fs.writeFile('my_directory/my_file.txt', 'Hello, world!');
console.log(fileEntry);
Example result for FileEntry<TData>
type:
{
type: 'file',
name: 'file.txt',
directory: 'root',
data: 'test 2 content',
createdAt: 1626882161631,
fullPath: 'root/file.txt'
}
fs.fileDetails(fullPath)
- Parameters: [
fullPath
: string] - Returns:
Promise<FileEntry<TData>>
- Description: Returns a promise that resolves to a
FileEntry
object containing details about the file specified by fullPath
. The FileEntry
object includes information such as the file's name, size, modification date, and type. If the file does not exist, or if the path contains directories that do not exist, the method will throw an error. - Throws an error when the path does not contain anything. This can happen if the
fullPath
parameter is an empty string or null
. - Throws an error when the destination file is not a file. For example, if the path points to a directory, the method will throw an error.
Example of usage:
import fs from 'indexeddb-fs';
const fileEntry = await fs.fileDetails('my_directory/my_file.txt');
console.log(fileEntry);
fs.readFile(fullPath)
- Parameters: [
fullPath
: string] - Returns:
Promise<TData>
- Description: Returns a promise that resolves to the contents of the file specified by
fullPath
. The returned data type is the same type with which it was saved. For example, if the file was saved as a string using the writeFile
method, the returned data type will also be a string. If the file was saved as an object using the writeFile
method, the returned data type will also be an object. - Throws an error when the destination file does not exist. This can happen if the file was deleted or if the
fullPath
parameter points to a file that does not exist yet. - Throws an error when the destination file is not a file. For example, if the path points to a directory, the method will throw an error.
Example of usage:
import fs from 'indexeddb-fs';
const fileContents = await fs.readFile('my_directory/my_file.txt');
console.log(fileContents);
fs.removeFile(fullPath)
- Parameters: [
fullPath
: string] - Returns:
Promise<void>
- Description: Removes the file specified by
fullPath
. The method returns a promise that resolves once the file has been successfully removed. - Throws an error when the path does not contain anything. This can happen if the
fullPath
parameter is an empty string or null
. - Throws an error when the destination file is not a file. For example, if the path points to a directory, the method will throw an error.
Example of usage:
import fs from 'indexeddb-fs';
await fs.removeFile('my_directory/my_file.txt');
fs.renameFile(fullPath, newFilename)
- Parameters: [
fullPath
: string, newFilename
: string] - Returns:
Promise<FileEntry<TData>>
- Description: Renames the file specified by
fullPath
to the new filename provided as newFilename
. The method returns a promise that resolves to a FileEntry
object representing the renamed file. - Throws an error when the path does not contain anything. This can happen if the
fullPath
parameter is an empty string or null
. - Throws an error when the
fullPath
is not a file. For example, if the path points to a directory, the method will throw an error. - Throws an error when the
fullPath
with a newFilename
is already taken. For example, if a file named newFilename
already exists in the same directory as the original file, the method will throw an error.
Example of usage:
import fs from 'indexeddb-fs';
const renamedFile = await fs.renameFile('my_directory/my_file.txt', 'new_file.txt');
console.log(renamedFile);
Example result for FileEntry<TData>
type:
{
type: 'file',
name: 'file.txt',
directory: 'root',
data: 'test 2 content',
createdAt: 1626882161631,
fullPath: 'root/file.txt'
}
fs.copyFile(fullPath, destinationPath)
- Parameters: [
fullPath
: string, destinationPath
: string] - Returns:
Promise<FileEntry<TData>>
- Description: Copies the file specified by
fullPath
to the destination path specified by destinationPath
. The method returns a promise that resolves to a FileEntry
object representing the copied file at the new destinationPath
. - Throws an error when the path does not contain anything. This can happen if the
fullPath
or destinationPath
parameters are empty strings or null
. - Throws an error when the
fullPath
is not a file. For example, if the path points to a directory, the method will throw an error. - Throws an error when the
destinationPath
is already taken. For example, if a file or directory already exists at the destinationPath
, the method will throw an error.
Example of usage:
import fs from 'indexeddb-fs';
const copiedFile = await fs.copyFile('my_directory/my_file.txt', 'my_directory/copied_file.txt');
console.log(copiedFile);
Example result for FileEntry<TData>
type:
{
type: 'file',
name: 'file.txt',
directory: 'root',
data: 'test 2 content',
createdAt: 1626882161631,
fullPath: 'root/file.txt'
}
fs.moveFile(fullPath, destinationPath)
- Parameters: [
fullPath
: string, destinationPath
: string] - Returns:
Promise<FileEntry<TData>>
- Description: Moves the file specified by
fullPath
to the destination path specified by destinationPath
. The method returns a promise that resolves to a FileEntry
object representing the moved file at the new destinationPath
. - Throws an error when the path does not contain anything. This can happen if the
fullPath
or destinationPath
parameters are empty strings or null
. - Throws an error when the
fullPath
is not a file. For example, if the path points to a directory, the method will throw an error. - Throws an error when the
destinationPath
is already taken. For example, if a file or directory already exists at the destinationPath
, the method will throw an error.
Example of usage:
import fs from 'indexeddb-fs';
const movedFile = await fs.moveFile('my_directory/my_file.txt', 'my_directory/moved_file.txt');
console.log(movedFile);
Example result for FileEntry<TData>
type:
{
type: 'file',
name: 'file.txt',
directory: 'root',
data: 'test 2 content',
createdAt: 1626882161631,
fullPath: 'root/file.txt'
}
Directory functions
fs.isDirectory(fullPath)
- Parameters: [
fullPath
: string] - Returns:
Promise<boolean>
- Description: Returns
true
if the path specified by fullPath
contains a directory, false
otherwise. - Throws an error when the path does not contain anything. This can happen if the
fullPath
parameter is an empty string or null
.
Example of usage:
import fs from 'indexeddb-fs';
const isDirectory = await fs.isDirectory('my_directory');
console.log(isDirectory);
fs.createDirectory(fullPath)
- Parameters: [
fullPath
: string] - Returns:
Promise<DirectoryEntry>
- Description: Creates a new directory at the path specified by
fullPath
and returns a Promise that resolves to a DirectoryEntry
object representing the new directory. - Throws an error when the destination directory of the directory does not exist. For example, if the
fullPath
parameter specifies a nested directory structure, the method will throw an error if any of the parent directories do not already exist. - Throws an error when the path contains a file with the same name. For example, if a file called
'my_directory'
already exists in the specified path, the method will throw an error.
Example of usage:
import fs from 'indexeddb-fs';
const newDirectory = await fs.createDirectory('my_directory');
console.log(newDirectory);
Example result for DirectoryEntry
type:
{
isRoot: false,
directory: 'root',
type: 'directory',
name: 'directory',
createdAt: 1626882291087,
fullPath: 'root/directory'
}
fs.readDirectory(fullPath)
- Parameters: [
fullPath
: string] - Returns:
Promise<ReadDirectoryDecoratorOutput>
- Description: Reads the entire contents of the directory specified by
fullPath
and returns a Promise that resolves to an object containing an array of DirectoryEntry
and FileEntry
objects representing the contents of the directory, as well as a count of the number of directories and files in the directory. - Throws an error when the destination directory does not exist. For example, if the
fullPath
parameter specifies a nested directory structure, the method will throw an error if any of the parent directories do not exist. - Throws an error when the destination directory is not a directory. For example, if the
fullPath
parameter specifies a file rather than a directory, the method will throw an error.
Example of usage:
import fs from 'indexeddb-fs';
const directoryContents = await fs.readDirectory('my_directory');
console.log(directoryContents);
Example result for ReadDirectoryDecoratorOutput
type:
{
files: [],
filesCount: 0,
isEmpty: true,
directories: [],
directoriesCount: 0,
}
fs.directoryDetails(fullPath)
- Parameters: [
fullPath
: string] - Returns:
Promise<DirectoryEntry>
- Description: Returns an object with details about the directory specified by
fullPath
. The method returns a Promise that resolves to a DirectoryEntry
object representing the directory. - Throws an error when the path does not contain anything. For example, if the
fullPath
parameter is an empty string or undefined, the method will throw an error. - Throws an error when the destination directory is not a directory. For example, if the
fullPath
parameter specifies a file rather than a directory, the method will throw an error.
Example of usage:
import fs from 'indexeddb-fs';
const directory = await fs.directoryDetails('my_directory');
console.log(directory);
Example result for DirectoryEntry
type:
{
isRoot: false,
directory: 'root',
type: 'directory',
name: 'directory',
createdAt: 1626882291087,
fullPath: 'root/directory'
}
fs.removeDirectory(fullPath)
- Parameters: [
fullPath
: string] - Returns:
Promise<void>
- Description: Removes the directory specified by
fullPath
, recursively removing any files/subdirectories contained within. The method returns a Promise that resolves once the directory has been removed. - Throws an error when the destination directory does not exist. For example, if the
fullPath
parameter specifies a nested directory structure, the method will throw an error if any of the parent directories do not exist. - Throws an error when the destination directory is not a directory. For example, if the
fullPath
parameter specifies a file rather than a directory, the method will throw an error.
Example of usage:
import fs from 'indexeddb-fs';
await fs.removeDirectory('my_directory');
License
This project is licensed under the MIT License - see the LICENSE file for details.