Compare commits

...

10 Commits

Author SHA1 Message Date
645ebbdd4d 3.0.6 2024-05-21 18:47:00 +02:00
168148b2c9 fix(core): update 2024-05-21 18:46:59 +02:00
1293fc4ca6 3.0.5 2024-05-21 18:42:55 +02:00
b040120813 fix(core): update 2024-05-21 18:42:55 +02:00
5c2d92c041 3.0.4 2024-05-21 01:22:22 +02:00
eaf2e7e6bb fix(core): update 2024-05-21 01:22:21 +02:00
1e1f65119c 3.0.3 2024-05-17 19:24:53 +02:00
c70ee820d7 fix(core): update 2024-05-17 19:24:52 +02:00
2a15362ced 3.0.2 2024-05-17 19:22:24 +02:00
9d5cdadd89 fix(core): update 2024-05-17 19:22:23 +02:00
14 changed files with 797 additions and 614 deletions

View File

@ -8,22 +8,30 @@
"githost": "code.foss.global",
"gitscope": "push.rocks",
"gitrepo": "smartbucket",
"description": "A TypeScript library for simple cloud independent object storage with support for buckets, directories, and files.",
"description": "A TypeScript library for cloud-independent object storage, providing features like bucket creation, file and directory management, and data streaming.",
"npmPackagename": "@push.rocks/smartbucket",
"license": "MIT",
"keywords": [
"cloud storage",
"object storage",
"minio",
"S3",
"TypeScript",
"smartstream",
"smartpromise",
"smartpath",
"smartrx",
"buckets",
"files management",
"directories management"
"S3",
"minio",
"file management",
"directory management",
"bucket creation",
"data streaming",
"multi-cloud",
"API",
"unified storage",
"buffer handling",
"access key",
"secret key",
"metadata",
"file locking",
"file streaming",
"directory listing",
"cloud agnostic"
]
}
},

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "@push.rocks/smartbucket",
"version": "3.0.1",
"version": "3.0.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@push.rocks/smartbucket",
"version": "3.0.1",
"version": "3.0.6",
"license": "UNLICENSED",
"dependencies": {
"@push.rocks/smartpath": "^5.0.18",

View File

@ -1,7 +1,7 @@
{
"name": "@push.rocks/smartbucket",
"version": "3.0.1",
"description": "A TypeScript library for simple cloud independent object storage with support for buckets, directories, and files.",
"version": "3.0.6",
"description": "A TypeScript library for cloud-independent object storage, providing features like bucket creation, file and directory management, and data streaming.",
"main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts",
"type": "module",
@ -12,17 +12,18 @@
"build": "(tsbuild --web --allowimplicitany)"
},
"devDependencies": {
"@git.zone/tsbuild": "^2.1.76",
"@git.zone/tsbuild": "^2.1.80",
"@git.zone/tsrun": "^1.2.46",
"@git.zone/tstest": "^1.0.90",
"@push.rocks/qenv": "^6.0.5",
"@push.rocks/tapbundle": "^5.0.23"
},
"dependencies": {
"@push.rocks/smartmime": "^2.0.0",
"@push.rocks/smartpath": "^5.0.18",
"@push.rocks/smartpromise": "^4.0.3",
"@push.rocks/smartrx": "^3.0.7",
"@push.rocks/smartstream": "^3.0.37",
"@push.rocks/smartstream": "^3.0.38",
"@tsclass/tsclass": "^4.0.54",
"minio": "^8.0.0"
},
@ -45,15 +46,23 @@
"keywords": [
"cloud storage",
"object storage",
"minio",
"S3",
"TypeScript",
"smartstream",
"smartpromise",
"smartpath",
"smartrx",
"buckets",
"files management",
"directories management"
"S3",
"minio",
"file management",
"directory management",
"bucket creation",
"data streaming",
"multi-cloud",
"API",
"unified storage",
"buffer handling",
"access key",
"secret key",
"metadata",
"file locking",
"file streaming",
"directory listing",
"cloud agnostic"
]
}

554
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

225
readme.md
View File

@ -1,5 +1,6 @@
# @push.rocks/smartbucket
simple cloud independent object storage
A TypeScript library that offers simple, cloud-independent object storage with features like bucket creation, file management, and directory management.
## Install
@ -13,15 +14,28 @@ This command will download and install `@push.rocks/smartbucket` along with its
## Usage
`@push.rocks/smartbucket` is a module designed to provide simple cloud-independent object storage functionality. It wraps various cloud storage providers such as AWS S3, Google Cloud Storage, and others, offering a unified API to manage storage buckets and objects within those buckets.
`@push.rocks/smartbucket` is a TypeScript module designed to provide simple cloud-independent object storage functionality. It wraps various cloud storage providers such as AWS S3, Google Cloud Storage, and others, offering a unified API to manage storage buckets and objects within those buckets.
To use `@push.rocks/smartbucket` in your project, you'll need to follow these general steps:
In this guide, we will delve into the usage of SmartBucket, covering its full range of features from setting up the library to advanced usage scenarios.
### Table of Contents
1. [Setting Up](#setting-up)
2. [Creating a New Bucket](#creating-a-new-bucket)
3. [Listing Buckets](#listing-buckets)
4. [Working with Files](#working-with-files)
- [Uploading Files](#uploading-files)
- [Downloading Files](#downloading-files)
- [Deleting Files](#deleting-files)
- [Streaming Files](#streaming-files)
5. [Working with Directories](#working-with-directories)
6. [Advanced Features](#advanced-features)
- [Bucket Policies](#bucket-policies)
- [Object Metadata](#object-metadata)
- [Cloud Agnostic](#cloud-agnostic)
### Setting Up
First, ensure you are using ECMAScript modules (ESM) and TypeScript in your project for best compatibility.
Here's how to import and initialize smartbucket in a TypeScript file:
First, ensure you are using ECMAScript modules (ESM) and TypeScript in your project for best compatibility. Here's how to import and initialize SmartBucket in a TypeScript file:
```typescript
import {
@ -35,10 +49,12 @@ const mySmartBucket = new SmartBucket({
accessKey: "yourAccessKey",
accessSecret: "yourSecretKey",
endpoint: "yourEndpointURL",
port: 443, // Default is 443, could be customized for specific endpoint
useSsl: true // Defaults to true
});
```
Make sure to replace `"yourAccessKey"`, `"yourSecretKey"`, and `"yourEndpointURL"` with your actual credentials and endpoint URL.
Make sure to replace `"yourAccessKey"`, `"yourSecretKey"`, and `"yourEndpointURL"` with your actual credentials and endpoint URL. The `port` and `useSsl` options are optional and can be omitted if the defaults are acceptable.
### Creating a New Bucket
@ -58,19 +74,15 @@ async function createBucket(bucketName: string) {
createBucket("exampleBucket");
```
**Important:** Bucket names must be unique across the storage service.
Bucket names must be unique across the storage service.
### Listing Buckets
To list all buckets:
Currently, SmartBucket does not include a direct method to list all buckets, but you can access the underlying client provided by the cloud storage SDK to perform such operations, depending on the SDK's capabilities.
```typescript
// Currently, SmartBucket does not include a direct method to list all buckets,
// but you can access the underlying client provided by the cloud storage SDK
// to perform such operations, depending on the SDK's capabilities.
```
### Working with Files
### Uploading Objects to a Bucket
#### Uploading Files
To upload an object to a bucket:
@ -78,8 +90,10 @@ To upload an object to a bucket:
async function uploadFile(bucketName: string, filePath: string, fileContent: Buffer | string) {
const myBucket: Bucket = await mySmartBucket.getBucketByName(bucketName);
if (myBucket) {
await myBucket.fastStore(filePath, fileContent);
await myBucket.fastPut({ path: filePath, contents: fileContent });
console.log(`File uploaded to ${bucketName} at ${filePath}`);
} else {
console.error(`Bucket ${bucketName} does not exist.`);
}
}
@ -87,7 +101,7 @@ async function uploadFile(bucketName: string, filePath: string, fileContent: Buf
uploadFile("exampleBucket", "path/to/object.txt", "Hello, world!");
```
### Downloading Objects from a Bucket
#### Downloading Files
To download an object:
@ -95,8 +109,10 @@ To download an object:
async function downloadFile(bucketName: string, filePath: string) {
const myBucket: Bucket = await mySmartBucket.getBucketByName(bucketName);
if (myBucket) {
const fileContent: Buffer = await myBucket.fastGet(filePath);
const fileContent: Buffer = await myBucket.fastGet({ path: filePath });
console.log("Downloaded file content:", fileContent.toString());
} else {
console.error(`Bucket ${bucketName} does not exist.`);
}
}
@ -104,7 +120,7 @@ async function downloadFile(bucketName: string, filePath: string) {
downloadFile("exampleBucket", "path/to/object.txt");
```
### Deleting Objects
#### Deleting Files
To delete an object from a bucket:
@ -112,8 +128,10 @@ To delete an object from a bucket:
async function deleteFile(bucketName: string, filePath: string) {
const myBucket: Bucket = await mySmartBucket.getBucketByName(bucketName);
if (myBucket) {
await myBucket.fastRemove(filePath);
await myBucket.fastRemove({ path: filePath });
console.log(`File at ${filePath} deleted from ${bucketName}.`);
} else {
console.error(`Bucket ${bucketName} does not exist.`);
}
}
@ -121,19 +139,176 @@ async function deleteFile(bucketName: string, filePath: string) {
deleteFile("exampleBucket", "path/to/object.txt");
```
#### Streaming Files
SmartBucket allows you to work with file streams, which can be useful for handling large files.
To read a file as a stream:
```typescript
import { ReplaySubject } from '@push.rocks/smartrx';
async function readFileStream(bucketName: string, filePath: string) {
const myBucket: Bucket = await mySmartBucket.getBucketByName(bucketName);
if (myBucket) {
const fileStream: ReplaySubject<Buffer> = await myBucket.fastGetStream({ path: filePath });
fileStream.subscribe({
next(chunk: Buffer) {
console.log("Chunk received:", chunk.toString());
},
complete() {
console.log("File read completed.");
},
error(err) {
console.error("Error reading file stream:", err);
}
});
} else {
console.error(`Bucket ${bucketName} does not exist.`);
}
}
// Use the function
readFileStream("exampleBucket", "path/to/object.txt");
```
To write a file as a stream:
```typescript
import { Readable } from 'stream';
async function writeFileStream(bucketName: string, filePath: string, readableStream: Readable) {
const myBucket: Bucket = await mySmartBucket.getBucketByName(bucketName);
if (myBucket) {
await myBucket.fastPutStream({ path: filePath, dataStream: readableStream });
console.log(`File streamed to ${bucketName} at ${filePath}`);
} else {
console.error(`Bucket ${bucketName} does not exist.`);
}
}
// Create a readable stream from a string
const readable = new Readable();
readable.push('Hello world streamed as a file!');
readable.push(null); // Indicates end of the stream
// Use the function
writeFileStream("exampleBucket", "path/to/streamedObject.txt", readable);
```
### Working with Directories
`@push.rocks/smartbucket` abstracts directories within buckets for easier object management. You can create, list, and delete directories using the `Directory` class.
### Additional Features
Here's how to list the contents of a directory:
- **Bucket Policies:** Manage bucket policies to control access permissions.
- **Object Metadata:** Retrieve and modify object metadata.
- **Cloud-Agnostic:** Designed to work with multiple cloud providers, allowing for easier migration or multi-cloud strategies.
```typescript
async function listDirectoryContents(bucketName: string, directoryPath: string) {
const myBucket: Bucket = await mySmartBucket.getBucketByName(bucketName);
if (myBucket) {
const baseDirectory: Directory = await myBucket.getBaseDirectory();
const targetDirectory: Directory = await baseDirectory.getSubDirectoryByName(directoryPath);
console.log('Listing directories:');
const directories = await targetDirectory.listDirectories();
directories.forEach(dir => {
console.log(`- ${dir.name}`);
});
console.log('Listing files:');
const files = await targetDirectory.listFiles();
files.forEach(file => {
console.log(`- ${file.name}`);
});
} else {
console.error(`Bucket ${bucketName} does not exist.`);
}
}
// Use the function
listDirectoryContents("exampleBucket", "some/directory/path");
```
To create a file within a directory:
```typescript
async function createFileInDirectory(bucketName: string, directoryPath: string, fileName: string, fileContent: string) {
const myBucket: Bucket = await mySmartBucket.getBucketByName(bucketName);
if (myBucket) {
const baseDirectory: Directory = await myBucket.getBaseDirectory();
const targetDirectory: Directory = await baseDirectory.getSubDirectoryByName(directoryPath);
await targetDirectory.createEmptyFile(fileName); // Create an empty file
const file = new File({ directoryRefArg: targetDirectory, fileName });
await file.updateWithContents({ contents: fileContent });
console.log(`File created: ${fileName}`);
} else {
console.error(`Bucket ${bucketName} does not exist.`);
}
}
// Use the function
createFileInDirectory("exampleBucket", "some/directory", "newfile.txt", "Hello, world!");
```
### Advanced Features
#### Bucket Policies
Manage bucket policies to control access permissions. This feature is dependent on the policies provided by the storage service (e.g., AWS S3, MinIO).
#### Object Metadata
You can retrieve and modify object metadata. Metadata can be useful for storing additional information about an object.
To retrieve metadata:
```typescript
async function getObjectMetadata(bucketName: string, filePath: string) {
const myBucket: Bucket = await mySmartBucket.getBucketByName(bucketName);
if (myBucket) {
const metadata = await mySmartBucket.minioClient.statObject(bucketName, filePath);
console.log("Object metadata:", metadata);
} else {
console.error(`Bucket ${bucketName} does not exist.`);
}
}
// Use the function
getObjectMetadata("exampleBucket", "path/to/object.txt");
```
To update metadata:
```typescript
async function updateObjectMetadata(bucketName: string, filePath: string, newMetadata: { [key: string]: string }) {
const myBucket: Bucket = await mySmartBucket.getBucketByName(bucketName);
if (myBucket) {
await myBucket.copyObject({
objectKey: filePath,
nativeMetadata: newMetadata,
deleteExistingNativeMetadata: false,
});
console.log(`Metadata updated for ${filePath}`);
} else {
console.error(`Bucket ${bucketName} does not exist.`);
}
}
// Use the function
updateObjectMetadata("exampleBucket", "path/to/object.txt", {
customKey: "customValue"
});
```
#### Cloud Agnostic
`@push.rocks/smartbucket` is designed to work with multiple cloud providers, allowing for easier migration or multi-cloud strategies. This means you can switch from one provider to another with minimal changes to your codebase.
Remember, each cloud provider has specific features and limitations. `@push.rocks/smartbucket` aims to abstract common functionalities, but always refer to the specific cloud provider's documentation for advanced features or limitations.
> **Note:** This document focuses on basic operations to get you started with `@push.rocks/smartbucket`. For advanced usage, including streaming data, managing bucket policies, and handling large file uploads, refer to the detailed API documentation and examples.
This guide covers the basic to advanced scenarios of using `@push.rocks/smartbucket`. For further details, refer to the API documentation and examples.
## License and Legal Information

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartbucket',
version: '3.0.1',
description: 'A TypeScript library for simple cloud independent object storage with support for buckets, directories, and files.'
version: '3.0.6',
description: 'A TypeScript library for cloud-independent object storage, providing features like bucket creation, file and directory management, and data streaming.'
}

View File

@ -1,6 +1,6 @@
import * as plugins from './smartbucket.plugins.js';
import { SmartBucket } from './smartbucket.classes.smartbucket.js';
import { Directory } from './smartbucket.classes.directory.js';
import * as plugins from './plugins.js';
import { SmartBucket } from './classes.smartbucket.js';
import { Directory } from './classes.directory.js';
export class Bucket {
public static async getBucketByName(smartbucketRef: SmartBucket, bucketNameArg: string) {
@ -52,15 +52,35 @@ export class Bucket {
public async fastPut(optionsArg: {
path: string;
contents: string | Buffer;
overwrite?: boolean;
}): Promise<void> {
try {
// Check if the object already exists
const exists = await this.fastExists({ path: optionsArg.path });
if (exists && !optionsArg.overwrite) {
console.error(`Object already exists at path '${optionsArg.path}' in bucket '${this.name}'.`);
return;
} else if (exists && optionsArg.overwrite) {
console.log(`Overwriting existing object at path '${optionsArg.path}' in bucket '${this.name}'.`);
} else {
console.log(`Creating new object at path '${optionsArg.path}' in bucket '${this.name}'.`);
}
// Proceed with putting the object
const streamIntake = new plugins.smartstream.StreamIntake();
const putPromise = this.smartbucketRef.minioClient
.putObject(this.name, optionsArg.path, streamIntake)
.catch((e) => console.log(e));
const putPromise = this.smartbucketRef.minioClient.putObject(this.name, optionsArg.path, streamIntake);
streamIntake.pushData(optionsArg.contents);
streamIntake.signalEnd();
const response = await putPromise;
await putPromise;
console.log(`Object '${optionsArg.path}' has been successfully stored in bucket '${this.name}'.`);
} catch (error) {
console.error(`Error storing object at path '${optionsArg.path}' in bucket '${this.name}':`, error);
throw error;
}
}
/**
* get file
@ -126,7 +146,22 @@ export class Bucket {
path: string;
dataStream: plugins.stream.Readable;
nativeMetadata?: { [key: string]: string };
overwrite?: boolean;
}): Promise<void> {
try {
// Check if the object already exists
const exists = await this.fastExists({ path: optionsArg.path });
if (exists && !optionsArg.overwrite) {
console.error(`Object already exists at path '${optionsArg.path}' in bucket '${this.name}'.`);
return;
} else if (exists && optionsArg.overwrite) {
console.log(`Overwriting existing object at path '${optionsArg.path}' in bucket '${this.name}'.`);
} else {
console.log(`Creating new object at path '${optionsArg.path}' in bucket '${this.name}'.`);
}
// Proceed with putting the object
await this.smartbucketRef.minioClient.putObject(
this.name,
optionsArg.path,
@ -139,7 +174,14 @@ export class Bucket {
})()
: {})
);
console.log(`Object '${optionsArg.path}' has been successfully stored in bucket '${this.name}'.`);
} catch (error) {
console.error(`Error storing object at path '${optionsArg.path}' in bucket '${this.name}':`, error);
throw error;
}
}
public async copyObject(optionsArg: {
/**
@ -198,7 +240,12 @@ export class Bucket {
await this.smartbucketRef.minioClient.removeObject(this.name, optionsArg.path);
}
public async doesObjectExist(optionsArg: {
/**
* check wether file exists
* @param optionsArg
* @returns
*/
public async fastExists(optionsArg: {
path: string;
}): Promise<boolean> {
try {
@ -215,4 +262,10 @@ export class Bucket {
}
}
}
public async fastStat(optionsArg: {
path: string;
}) {
return this.smartbucketRef.minioClient.statObject(this.name, optionsArg.path);
}
}

View File

@ -1,6 +1,6 @@
import * as plugins from './smartbucket.plugins.js';
import { Bucket } from './smartbucket.classes.bucket.js';
import { File } from './smartbucket.classes.file.js';
import * as plugins from './plugins.js';
import { Bucket } from './classes.bucket.js';
import { File } from './classes.file.js';
export class Directory {
public bucketRef: Bucket;
@ -59,6 +59,32 @@ export class Directory {
return basePath;
}
/**
* gets a file by name
*/
public async getFile(optionsArg: {
name: string;
createWithContents?: string | Buffer;
}): Promise<File> {
// check wether the file exists
const exists = await this.bucketRef.fastExists({
path: this.getBasePath() + optionsArg.name,
});
if (!exists && !optionsArg.createWithContents) {
return null;
}
if (!exists && optionsArg.createWithContents) {
await this.fastPut({
path: optionsArg.name,
contents: optionsArg.createWithContents,
});
}
return new File({
directoryRefArg: this,
fileName: optionsArg.name,
})
}
/**
* lists all files
*/

175
ts/classes.file.ts Normal file
View File

@ -0,0 +1,175 @@
import * as plugins from './plugins.js';
import { Directory } from './classes.directory.js';
import { MetaData } from './classes.metadata.js';
/**
* represents a file in a directory
*/
export class File {
// STATIC
/**
* creates a file in draft mode
* you need to call .save() to store it in s3
* @param optionsArg
*/
public static async create(optionsArg: {
directory: Directory;
name: string;
contents: Buffer | string | plugins.stream.Readable;
/**
* if contents are of type string, you can specify the encoding here
*/
encoding?: 'utf8' | 'binary';
}): Promise<File> {
const contents =
typeof optionsArg.contents === 'string'
? Buffer.from(optionsArg.contents, optionsArg.encoding)
: optionsArg.contents;
const file = new File({
directoryRefArg: optionsArg.directory,
fileName: optionsArg.name,
});
if (contents instanceof plugins.stream.Readable) {} else {
await optionsArg.directory.fastPut({
path: optionsArg.name,
contents: contents,
});
}
return file;
}
// INSTANCE
public parentDirectoryRef: Directory;
public name: string;
public getBasePath(): string {
return plugins.path.join(this.parentDirectoryRef.getBasePath(), this.name);
};
constructor(optionsArg: { directoryRefArg: Directory; fileName: string }) {
this.parentDirectoryRef = optionsArg.directoryRefArg;
this.name = optionsArg.fileName;
}
public async getContentsAsString(): Promise<string> {
const fileBuffer = await this.getContents();
return fileBuffer.toString();
}
public async getContents(): Promise<Buffer> {
const resultBuffer = await this.parentDirectoryRef.bucketRef.fastGet({
path: this.getBasePath(),
});
return resultBuffer;
}
public async getReadStream() {
const readStream = this.parentDirectoryRef.bucketRef.fastGetStream({
path: this.getBasePath(),
});
}
/**
* removes this file
* for using recycling mechanics use .delete()
*/
public async remove() {
await this.parentDirectoryRef.bucketRef.fastRemove({
path: this.getBasePath(),
});
if (!this.name.endsWith('.metadata')) {
await this.parentDirectoryRef.bucketRef.fastRemove({
path: this.getBasePath() + '.metadata',
});
}
await this.parentDirectoryRef.listFiles();
}
/**
* deletes the file with recycling mechanics
*/
public async delete() {
await this.remove();
}
/**
* allows locking the file
* @param optionsArg
*/
public async lock(optionsArg?: { timeoutMillis?: number }) {
const metadata = await this.getMetaData();
await metadata.setLock({
lock: 'locked',
expires: Date.now() + (optionsArg?.timeoutMillis || 1000),
});
}
/**
* actively unlocks a file
*
*/
public async unlock(optionsArg?: {
/**
* unlock the file even if not locked from this instance
*/
force?: boolean;
}) {
const metadata = await this.getMetaData();
await metadata.removeLock({
force: optionsArg?.force,
});
}
public async updateWithContents(optionsArg: {
contents: Buffer | string | plugins.stream.Readable;
encoding?: 'utf8' | 'binary';
}) {
if (optionsArg.contents instanceof plugins.stream.Readable) {
await this.parentDirectoryRef.bucketRef.fastPutStream({
path: this.getBasePath(),
dataStream: optionsArg.contents,
});
} else if (Buffer.isBuffer(optionsArg.contents)) {
await this.parentDirectoryRef.bucketRef.fastPut({
path: this.getBasePath(),
contents: optionsArg.contents,
});
} else if (typeof optionsArg.contents === 'string') {
await this.parentDirectoryRef.bucketRef.fastPut({
path: this.getBasePath(),
contents: Buffer.from(optionsArg.contents, optionsArg.encoding),
});
}
}
/**
* allows updating the metadata of a file
* @param updatedMetadata
*/
public async getMetaData() {
if (this.name.endsWith('.metadata')) {
throw new Error('metadata files cannot have metadata');
}
const metadata = await MetaData.createForFile({
file: this,
});
return metadata;
}
/**
* gets the contents as json
*/
public async getJsonData() {
const json = await this.getContentsAsString();
const parsed = await JSON.parse(json);
return parsed;
}
public async writeJsonData(dataArg: any) {
await this.updateWithContents({
contents: JSON.stringify(dataArg),
});
}
}

100
ts/classes.metadata.ts Normal file
View File

@ -0,0 +1,100 @@
import * as plugins from './plugins.js';
import { File } from './classes.file.js';
export class MetaData {
// static
public static async createForFile(optionsArg: { file: File }) {
const metaData = new MetaData();
metaData.fileRef = optionsArg.file;
// lets find the existing metadata file
metaData.metadataFile = await metaData.fileRef.parentDirectoryRef.getFile({
name: metaData.fileRef.name + '.metadata',
createWithContents: '{}',
});
return metaData;
}
// instance
/**
* the file that contains the metadata
*/
metadataFile: File;
/**
* the file that the metadata is for
*/
fileRef: File;
public async getFileType(optionsArg?: {
useFileExtension?: boolean;
useMagicBytes?: boolean;
}): Promise<string> {
if ((optionsArg && optionsArg.useFileExtension) || optionsArg.useFileExtension === undefined) {
return plugins.path.extname(this.fileRef.name);
}
}
/**
* gets the size of the fileRef
*/
public async getSizeInBytes(): Promise<number> {
const stat = await this.fileRef.parentDirectoryRef.bucketRef.fastStat({
path: this.fileRef.getBasePath(),
});
return stat.size;
}
private prefixCustomMetaData = 'custom_';
public async storeCustomMetaData<T = any>(optionsArg: { key: string; value: T }) {
const data = await this.metadataFile.getContentsAsString();
data[this.prefixCustomMetaData + optionsArg.key] = optionsArg.value;
await this.metadataFile.writeJsonData(data);
}
public async getCustomMetaData<T = any>(optionsArg: { key: string }): Promise<T> {
const data = await this.metadataFile.getJsonData();
return data[this.prefixCustomMetaData + optionsArg.key];
}
public async deleteCustomMetaData(optionsArg: { key: string }) {
const data = await this.metadataFile.getJsonData();
delete data[this.prefixCustomMetaData + optionsArg.key];
await this.metadataFile.writeJsonData(data);
}
/**
* set a lock on the ref file
* @param optionsArg
*/
public async setLock(optionsArg: { lock: string; expires: number }) {
const data = await this.metadataFile.getJsonData();
data.lock = optionsArg.lock;
data.lockExpires = optionsArg.expires;
await this.metadataFile.writeJsonData(data);
}
/**
* remove the lock on the ref file
* @param optionsArg
*/
public async removeLock(optionsArg: { force: boolean }) {
const data = await this.metadataFile.getJsonData();
delete data.lock;
delete data.lockExpires;
await this.metadataFile.writeJsonData(data);
}
public async checkLocked(): Promise<boolean> {
const data = await this.metadataFile.getJsonData();
return data.lock && data.lockExpires > Date.now();
}
public async getLockInfo(): Promise<{ lock: string; expires: number }> {
const data = await this.metadataFile.getJsonData();
return { lock: data.lock, expires: data.lockExpires };
}
}

View File

@ -1,5 +1,5 @@
import * as plugins from './smartbucket.plugins.js';
import { Bucket } from './smartbucket.classes.bucket.js';
import * as plugins from './plugins.js';
import { Bucket } from './classes.bucket.js';
export class SmartBucket {
public config: plugins.tsclass.storage.IS3Descriptor;

View File

@ -1,4 +1,4 @@
export * from './smartbucket.classes.smartbucket.js';
export * from './smartbucket.classes.bucket.js';
export * from './smartbucket.classes.directory.js';
export * from './smartbucket.classes.file.js';
export * from './classes.smartbucket.js';
export * from './classes.bucket.js';
export * from './classes.directory.js';
export * from './classes.file.js';

View File

@ -5,12 +5,13 @@ import * as stream from 'stream';
export { path, stream };
// @push.rocks scope
import * as smartmime from '@push.rocks/smartmime';
import * as smartpath from '@push.rocks/smartpath';
import * as smartpromise from '@push.rocks/smartpromise';
import * as smartrx from '@push.rocks/smartrx';
import * as smartstream from '@push.rocks/smartstream';
export { smartpath, smartpromise, smartrx, smartstream };
export { smartmime, smartpath, smartpromise, smartrx, smartstream };
// @tsclass
import * as tsclass from '@tsclass/tsclass';

View File

@ -1,140 +0,0 @@
import * as plugins from './smartbucket.plugins.js';
import { Directory } from './smartbucket.classes.directory.js';
export interface IFileMetaData {
name: string;
fileType: string;
size: string;
}
/**
* represents a file in a directory
*/
export class File {
// STATIC
/**
* creates a file in draft mode
* you need to call .save() to store it in s3
* @param optionsArg
*/
public static async create(optionsArg: {
directory: Directory;
name: string;
contents: Buffer | string | plugins.stream.Readable;
/**
* if contents are of type string, you can specify the encoding here
*/
encoding?: 'utf8' | 'binary';
}): Promise<File> {
const contents =
typeof optionsArg.contents === 'string'
? Buffer.from(optionsArg.contents, optionsArg.encoding)
: optionsArg.contents;
const file = new File({
directoryRefArg: optionsArg.directory,
fileName: optionsArg.name,
});
if (contents instanceof plugins.stream.Readable) {} else {
await optionsArg.directory.fastPut({
path: optionsArg.name,
contents: contents,
});
}
return file;
}
// INSTANCE
public parentDirectoryRef: Directory;
public name: string;
public path: string;
public metaData: IFileMetaData;
constructor(optionsArg: { directoryRefArg: Directory; fileName: string }) {
this.parentDirectoryRef = optionsArg.directoryRefArg;
this.name = optionsArg.fileName;
}
public async getContentAsString() {
const fileBuffer = await this.getContentAsBuffer();
return fileBuffer.toString();
}
public async getContentAsBuffer() {
const done = plugins.smartpromise.defer();
const fileStream = await this.parentDirectoryRef.bucketRef.smartbucketRef.minioClient
.getObject(this.parentDirectoryRef.bucketRef.name, this.path)
.catch((e) => console.log(e));
let completeFile = Buffer.from('');
const duplexStream = new plugins.smartstream.SmartDuplex<Buffer, Buffer>(
{
writeFunction: async (chunk) => {
completeFile = Buffer.concat([chunk]);
return chunk;
},
finalFunction: async (cb) => {
done.resolve();
return Buffer.from('');
},
}
);
if (!fileStream) {
return null;
}
fileStream.pipe(duplexStream);
await done.promise;
return completeFile;
}
public async readStreaming() {
// TODO
throw new Error('not yet implemented');
}
/**
* removes this file
*/
public async remove() {
await this.parentDirectoryRef.bucketRef.smartbucketRef.minioClient.removeObject(
this.parentDirectoryRef.bucketRef.name,
this.path
);
await this.parentDirectoryRef.listFiles();
}
/**
* deletes the file
*/
public async delete() {}
/**
* allows locking the file
* @param optionsArg
*/
public async lock(optionsArg?: { timeoutMillis?: number }) {}
/**
* actively unlocks a file
*
*/
public async unlock(optionsArg?: {
/**
* unlock the file even if not locked from this instance
*/
force?: boolean;
}) {}
public async updateWithContents(optionsArg: {
contents: Buffer | string | plugins.stream.Readable;
encoding?: 'utf8' | 'binary';
}) {}
/**
* allows updating the metadata of a file
* @param updatedMetadata
*/
public async updateMetaData(updatedMetadata: any) {}
}