# <a name='storage-abstraction' rel='nofollow' onclick='return false;'></a>Storage Abstraction
Provides an abstraction layer for interacting with a storage; this storage can be a local file system or a cloud storage. Currently local disk storage, Backblaze B2, Google Cloud and Amazon S3 and compliant cloud services are supported.
Because the API only provides basic storage operations (see [below](#api-methods)) the API is cloud agnostic. This means for instance that you can develop your application using storage on local disk and then use Google Cloud or Amazon S3 in your production environment without changing any code.
## <a name='table-of-contents' rel='nofollow' onclick='return false;'></a>Table of contents
<!-- toc -->
- [Instantiate a storage](#instantiate-a-storage)
* [Configuration object](#configuration-object)
* [Configuration URL](#configuration-url)
- [Adapters](#adapters)
* [Local storage](#local-storage)
* [Google Cloud](#google-cloud)
* [Amazon S3](#amazon-s3)
* [Backblaze B2](#backblaze-b2)
- [API methods](#api-methods)
* [init](#init)
* [test](#test)
* [createBucket](#createbucket)
* [selectBucket](#selectbucket)
* [clearBucket](#clearbucket)
* [deleteBucket](#deletebucket)
* [listBuckets](#listbuckets)
* [getSelectedBucket](#getselectedbucket)
* [addFileFromPath](#addfilefrompath)
* [addFileFromBuffer](#addfilefrombuffer)
* [addFileFromReadable](#addfilefromreadable)
* [getFileAsReadable](#getfileasreadable)
* [removeFile](#removefile)
* [sizeOf](#sizeof)
* [fileExists](#fileexists)
* [listFiles](#listfiles)
* [getType](#gettype)
* [getConfiguration](#getconfiguration)
* [switchAdapter](#switchadapter)
- [How it works](#how-it-works)
- [Adding more adapters](#adding-more-adapters)
* [Define your configuration](#define-your-configuration)
* [Adapter class](#adapter-class)
* [Adapter function](#adapter-function)
* [Register your adapter](#register-your-adapter)
- [Tests](#tests)
- [Example application](#example-application)
- [Questions and requests](#questions-and-requests)
<!-- tocstop -->
## <a name='instantiate-a-storage' rel='nofollow' onclick='return false;'></a>Instantiate a storage
```javascript
const s = new Storage(config);
```
When instantiating a new `Storage` the argument `config` is used to create an adapter that translates the generic API calls to storage specific calls. You can provide the `config` argument in 2 forms:
1. using a configuration object (js: `typeof === "object"` ts: `AdapterConfig`)
2. using a configuration URL (`typeof === "string"`)
Internally configuration URL will be converted to a configuration object so any rule that applies to a configuration object also applies to configuration URLs.
The configuration must specify a type; the type is used to create the appropriate adapter. The value of the type is one of the enum members of `StorageType`:
```typescript
enum StorageType {
LOCAL = "local",
GCS = "gcs",
S3 = "s3",
B2 = "b2",
}
```
### <a name='configuration-object' rel='nofollow' onclick='return false;'></a>Configuration object
A configuration object extends `IAdapterConfig`:
```typescript
interface IAdapterConfig {
type: string;
slug?: boolean;
bucketName?: string;
}
```
Besides the mandatory key `type` one or more keys may be mandatory or optional dependent on the type of storage; for instance keys for passing credentials such as `keyFilename` for Google Storage or `accessKeyId` and `secretAccessKey` for Amazon S3, and keys for further configuring the storage service such as `sslEnabled` for Amazon S3.
The optional key `slug` determines if bucket names and paths to files in buckets are slugified automatically.
Another optional key is `bucketName`; for most cloud storage services it is required to select a bucket after a connection to the service has been made. If you don't want or can't provide a bucket name on initialization you can use `selectBucket` to do so afterwards.
### <a name='configuration-url' rel='nofollow' onclick='return false;'></a>Configuration URL
Configuration urls always start with a protocol that defines the type of storage:
- `local://` → local storage
- `gcs://` → Google Cloud
- `s3://` → Amazon S3
- `b2://` → Backblaze B2
These values match the values in the enum `StorageType` shown above. What follows after the protocol is the part that contains the configuration of the storage. The format of the URL differs per storage type, see the documentation per adapter [below](#adapters) for details.
```typescript
// local storage
const url = "local://path/to/bucket";
// Amazon S3
const url =
"s3://accessKeyId:secretAccessKey@region/bucketName?extraOption1=value1&extraOption2=value2...";
// Google Cloud Storage
const url =
"gcs://path/to/keyFile.json:projectId@bucketName?extraOption1=value1&extraOption2=value2...";
// Backblaze B2
const url =
"b2://applicationKeyId:applicationKey@bucketName?extraOption1=value1&extraOption2=value2...";
```
## <a name='adapters' rel='nofollow' onclick='return false;'></a>Adapters
The adapters are the key part of this library; where the `Storage` is merely a thin wrapper (see [How it works](#how-it-works)), adapters perform the actual actions on the storage by translating generic API methods calls to storage specific calls.
Below follows a description of the available adapters; what the configuration objects and URLs look like and what the default values are. Also per adapter the peer dependencies are listed as a handy copy-pasteble npm command. The peer dependencies are usually wrapper libraries such as aws-sdk but can also be specific modules with a specific functionality such as rimraf for local storage.
If you want to use one or more of the adapters in your project make sure you install the required peer dependencies. By installing only the dependencies that you will actually use, your project codebase will stay as slim and maintainable as possible.
You can also add more adapters yourself very easily, see [below](#adding-more-adapters)
### <a name='local-storage' rel='nofollow' onclick='return false;'></a>Local storage
> peer dependencies: <br/> > `npm i glob rimraf`
Configuration object:
```typescript
type ConfigLocal = {
type: StorageType;
directory: string;
slug?: boolean;
mode?: string | number;
};
```
Configuration url:
```typescript
const url = "local://path/to/your/bucket?mode=750";
```
The key `mode` is used to set the access rights when creating new buckets. The default value is `0o777`. You can pass this value both as a string and as a number.
If you use a configuration URL you can only pass values as strings; string values without radix prefix will be interpreted as octal numbers, so "750" is the same as "0o750" and both yield the same numeric value `0o750` or `488` decimal.
When using a configuration object you can also pass `mode` as a number, please don't forget the radix prefix if you don't use decimal numbers, e.g. `750` is probably not what you want, pass `0o750` or `488` instead. Best is to use strings to avoid confusion.
Example:
```typescript
const config = {
type: StorageType.LOCAL,
directory: "path/to/folder/bucket",
mode: "750",
};
const s = new Storage(config);
// or
const url = "local://path/to/folder/bucket?mode=750";
const s = new Storage(url);
```
Files will be stored in `path/to/folder/bucket`, folders will be created if necessary. As you can see the last folder of the directory will be used as bucket; if you call `getSelectedBucket()` the name of this folder will be returned.
Note the use of double and triple slashes:
```typescript
// example #2
const s = new Storage {
type: StorageType.LOCAL,
directory: "files",
};
const s = new Storage("local://files") // note: 2 slashes
s.getConfiguration().directory; // folder where the process runs, process.cwd()
s.getConfiguration().bucketName; // 'files'
// example #3
const s = new Storage {
type: StorageType.LOCAL,
directory: "/files",
};
const s = new Storage("local:///files") // note: 3 slashes
s.getConfiguration().directory; // '/' root folder (may require extra permissions)
s.getConfiguration().bucketName; // 'files'
```
### <a name='google-cloud' rel='nofollow' onclick='return false;'></a>Google Cloud
> p