Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
1e1f65119c | |||
c70ee820d7 | |||
2a15362ced | |||
9d5cdadd89 | |||
a92fae2617 | |||
2cacfcf990 | |||
72d1e1e5a2 | |||
a0be96bf23 |
@ -8,22 +8,25 @@
|
||||
"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 that offers simple, cloud-independent object storage with features like bucket creation, file management, and directory management.",
|
||||
"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"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
17911
package-lock.json
generated
17911
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
42
package.json
42
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@push.rocks/smartbucket",
|
||||
"version": "2.0.5",
|
||||
"description": "A TypeScript library for simple cloud independent object storage with support for buckets, directories, and files.",
|
||||
"version": "3.0.3",
|
||||
"description": "A TypeScript library that offers simple, cloud-independent object storage with features like bucket creation, file management, and directory management.",
|
||||
"main": "dist_ts/index.js",
|
||||
"typings": "dist_ts/index.d.ts",
|
||||
"type": "module",
|
||||
@ -12,20 +12,19 @@
|
||||
"build": "(tsbuild --web --allowimplicitany)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@git.zone/tsbuild": "^2.1.63",
|
||||
"@git.zone/tsbuild": "^2.1.80",
|
||||
"@git.zone/tsrun": "^1.2.46",
|
||||
"@git.zone/tstest": "^1.0.71",
|
||||
"@push.rocks/qenv": "^6.0.4",
|
||||
"@push.rocks/tapbundle": "^5.0.3"
|
||||
"@git.zone/tstest": "^1.0.90",
|
||||
"@push.rocks/qenv": "^6.0.5",
|
||||
"@push.rocks/tapbundle": "^5.0.23"
|
||||
},
|
||||
"dependencies": {
|
||||
"@push.rocks/smartpath": "^5.0.5",
|
||||
"@push.rocks/smartpath": "^5.0.18",
|
||||
"@push.rocks/smartpromise": "^4.0.3",
|
||||
"@push.rocks/smartrx": "^3.0.7",
|
||||
"@push.rocks/smartstream": "^2.0.2",
|
||||
"@tsclass/tsclass": "^4.0.50",
|
||||
"@types/minio": "^7.0.13",
|
||||
"minio": "^7.0.28"
|
||||
"@push.rocks/smartstream": "^3.0.38",
|
||||
"@tsclass/tsclass": "^4.0.54",
|
||||
"minio": "^8.0.0"
|
||||
},
|
||||
"private": false,
|
||||
"files": [
|
||||
@ -46,15 +45,18 @@
|
||||
"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"
|
||||
]
|
||||
}
|
||||
|
9250
pnpm-lock.yaml
generated
9250
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
219
readme.md
219
readme.md
@ -1,5 +1,5 @@
|
||||
# @push.rocks/smartbucket
|
||||
simple cloud independent object storage
|
||||
A TypeScript library for simple cloud independent object storage with support for buckets, directories, and files.
|
||||
|
||||
## Install
|
||||
|
||||
@ -13,15 +13,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 +48,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 +73,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 +89,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 +100,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 +108,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 +119,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 +127,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 +138,171 @@ 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
|
||||
```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}`);
|
||||
});
|
||||
|
||||
- **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.
|
||||
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
|
||||
|
||||
|
65
test/test.ts
65
test/test.ts
@ -33,28 +33,55 @@ tap.test('should get a bucket', async () => {
|
||||
|
||||
// Fast operations
|
||||
tap.test('should store data in bucket fast', async () => {
|
||||
await myBucket.fastPut('hithere/socool.txt', 'hi there!');
|
||||
await myBucket.fastPut({
|
||||
path: 'hithere/socool.txt',
|
||||
contents: 'hi there!',
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('should get data in bucket', async () => {
|
||||
const fileString = await myBucket.fastGet('hithere/socool.txt');
|
||||
const fileStringStream = await myBucket.fastGetStream('hithere/socool.txt');
|
||||
const fileString = await myBucket.fastGet({
|
||||
path: 'hithere/socool.txt',
|
||||
});
|
||||
const fileStringStream = await myBucket.fastGetStream({
|
||||
path: 'hithere/socool.txt',
|
||||
});
|
||||
console.log(fileString);
|
||||
});
|
||||
|
||||
tap.test('should delete data in bucket', async () => {
|
||||
await myBucket.fastRemove('hithere/socool.txt');
|
||||
await myBucket.fastRemove({
|
||||
path: 'hithere/socool.txt',
|
||||
});
|
||||
});
|
||||
|
||||
// fs operations
|
||||
|
||||
tap.test('prepare for directory style tests', async () => {
|
||||
await myBucket.fastPut('dir1/file1.txt', 'dir1/file1.txt content');
|
||||
await myBucket.fastPut('dir1/file2.txt', 'dir1/file2.txt content');
|
||||
await myBucket.fastPut('dir2/file1.txt', 'dir2/file1.txt content');
|
||||
await myBucket.fastPut('dir3/file1.txt', 'dir3/file1.txt content');
|
||||
await myBucket.fastPut('dir3/dir4/file1.txt', 'dir3/dir4/file1.txt content');
|
||||
await myBucket.fastPut('file1.txt', 'file1 content');
|
||||
await myBucket.fastPut({
|
||||
path: 'dir1/file1.txt',
|
||||
contents: 'dir1/file1.txt content',
|
||||
});
|
||||
await myBucket.fastPut({
|
||||
path: 'dir1/file2.txt',
|
||||
contents: 'dir1/file2.txt content',
|
||||
});
|
||||
await myBucket.fastPut({
|
||||
path: 'dir2/file1.txt',
|
||||
contents: 'dir2/file1.txt content',
|
||||
});
|
||||
await myBucket.fastPut({
|
||||
path: 'dir3/file1.txt',
|
||||
contents: 'dir3/file1.txt content',
|
||||
});
|
||||
await myBucket.fastPut({
|
||||
path: 'dir3/dir4/file1.txt',
|
||||
contents: 'dir3/dir4/file1.txt content',
|
||||
});
|
||||
await myBucket.fastPut({
|
||||
path: 'file1.txt',
|
||||
contents: 'file1 content',
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('should get base directory', async () => {
|
||||
@ -77,12 +104,18 @@ tap.test('should correctly build paths for sub directories', async () => {
|
||||
});
|
||||
|
||||
tap.test('clean up directory style tests', async () => {
|
||||
await myBucket.fastRemove('dir1/file1.txt');
|
||||
await myBucket.fastRemove('dir1/file2.txt');
|
||||
await myBucket.fastRemove('dir2/file1.txt');
|
||||
await myBucket.fastRemove('dir3/file1.txt');
|
||||
await myBucket.fastRemove('dir3/dir4/file1.txt');
|
||||
await myBucket.fastRemove('file1.txt');
|
||||
await myBucket.fastRemove({
|
||||
path: 'dir1/file1.txt',
|
||||
});
|
||||
await myBucket.fastRemove({
|
||||
path: 'dir1/file2.txt',
|
||||
});
|
||||
await myBucket.fastRemove({
|
||||
path: 'dir2/file1.txt',
|
||||
});
|
||||
await myBucket.fastRemove({ path: 'dir3/file1.txt' });
|
||||
await myBucket.fastRemove({ path: 'dir3/dir4/file1.txt' });
|
||||
await myBucket.fastRemove({ path: 'file1.txt' });
|
||||
});
|
||||
|
||||
tap.start();
|
||||
|
@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartbucket',
|
||||
version: '2.0.5',
|
||||
description: 'A TypeScript library for simple cloud independent object storage with support for buckets, directories, and files.'
|
||||
version: '3.0.3',
|
||||
description: 'A TypeScript library that offers simple, cloud-independent object storage with features like bucket creation, file management, and directory management.'
|
||||
}
|
||||
|
@ -49,58 +49,63 @@ export class Bucket {
|
||||
/**
|
||||
* store file
|
||||
*/
|
||||
public async fastPut(pathArg: string, fileContent: string | Buffer): Promise<void> {
|
||||
public async fastPut(optionsArg: {
|
||||
path: string;
|
||||
contents: string | Buffer;
|
||||
}): Promise<void> {
|
||||
const streamIntake = new plugins.smartstream.StreamIntake();
|
||||
const putPromise = this.smartbucketRef.minioClient
|
||||
.putObject(this.name, pathArg, streamIntake.getReadable())
|
||||
.putObject(this.name, optionsArg.path, streamIntake)
|
||||
.catch((e) => console.log(e));
|
||||
streamIntake.pushData(fileContent);
|
||||
streamIntake.pushData(optionsArg.contents);
|
||||
streamIntake.signalEnd();
|
||||
await putPromise;
|
||||
const response = await putPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* get file
|
||||
*/
|
||||
public async fastGet(pathArg: string): Promise<Buffer> {
|
||||
public async fastGet(optionsArg: Parameters<typeof this.fastGetStream>[0]): Promise<Buffer> {
|
||||
const done = plugins.smartpromise.defer();
|
||||
let completeFile: Buffer;
|
||||
const replaySubject = await this.fastGetStream(pathArg);
|
||||
const subscription = replaySubject.subscribe(
|
||||
(chunk) => {
|
||||
const replaySubject = await this.fastGetStream(optionsArg);
|
||||
const subscription = replaySubject.subscribe({
|
||||
next: (chunk) => {
|
||||
if (completeFile) {
|
||||
completeFile = Buffer.concat([completeFile, chunk]);
|
||||
} else {
|
||||
completeFile = chunk;
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
console.log(err);
|
||||
},
|
||||
() => {
|
||||
complete: () => {
|
||||
done.resolve();
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
);
|
||||
},
|
||||
error: (err) => {
|
||||
console.log(err);
|
||||
},
|
||||
});
|
||||
await done.promise;
|
||||
return completeFile;
|
||||
}
|
||||
|
||||
public async fastGetStream(pathArg: string): Promise<plugins.smartrx.rxjs.ReplaySubject<Buffer>> {
|
||||
public async fastGetStream(optionsArg: {
|
||||
path: string;
|
||||
}): Promise<plugins.smartrx.rxjs.ReplaySubject<Buffer>> {
|
||||
const fileStream = await this.smartbucketRef.minioClient
|
||||
.getObject(this.name, pathArg)
|
||||
.getObject(this.name, optionsArg.path)
|
||||
.catch((e) => console.log(e));
|
||||
const replaySubject = new plugins.smartrx.rxjs.ReplaySubject<Buffer>();
|
||||
const duplexStream = plugins.smartstream.createDuplexStream<Buffer, Buffer>(
|
||||
async (chunk) => {
|
||||
const duplexStream = new plugins.smartstream.SmartDuplex<Buffer, void>({
|
||||
writeFunction: async (chunk) => {
|
||||
replaySubject.next(chunk);
|
||||
return chunk;
|
||||
return;
|
||||
},
|
||||
async (cb) => {
|
||||
finalFunction: async (cb) => {
|
||||
replaySubject.complete();
|
||||
return Buffer.from('');
|
||||
return;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (!fileStream) {
|
||||
return null;
|
||||
@ -109,7 +114,6 @@ export class Bucket {
|
||||
const smartstream = new plugins.smartstream.StreamWrapper([
|
||||
fileStream,
|
||||
duplexStream,
|
||||
plugins.smartstream.cleanPipe(),
|
||||
]);
|
||||
smartstream.run();
|
||||
return replaySubject;
|
||||
@ -119,16 +123,16 @@ export class Bucket {
|
||||
* store file as stream
|
||||
*/
|
||||
public async fastPutStream(optionsArg: {
|
||||
pathArg: string;
|
||||
path: string;
|
||||
dataStream: plugins.stream.Readable;
|
||||
metadata?: { [key: string]: string };
|
||||
nativeMetadata?: { [key: string]: string };
|
||||
}): Promise<void> {
|
||||
await this.smartbucketRef.minioClient.putObject(
|
||||
this.name,
|
||||
optionsArg.pathArg,
|
||||
optionsArg.path,
|
||||
optionsArg.dataStream,
|
||||
null,
|
||||
...(optionsArg.metadata
|
||||
...(optionsArg.nativeMetadata
|
||||
? (() => {
|
||||
const returnObject: any = {};
|
||||
return returnObject;
|
||||
@ -137,30 +141,47 @@ export class Bucket {
|
||||
);
|
||||
}
|
||||
|
||||
public async updateMetadata(
|
||||
bucket: string,
|
||||
objectKey: string,
|
||||
metadata: { [key: string]: string }
|
||||
): Promise<void> {
|
||||
public async copyObject(optionsArg: {
|
||||
/**
|
||||
* the
|
||||
*/
|
||||
objectKey: string;
|
||||
/**
|
||||
* in case you want to copy to another bucket specify it here
|
||||
*/
|
||||
targetBucket?: Bucket;
|
||||
targetBucketKey?: string;
|
||||
/**
|
||||
* metadata will be merged with existing metadata
|
||||
*/
|
||||
nativeMetadata?: { [key: string]: string };
|
||||
deleteExistingNativeMetadata?: boolean;
|
||||
}): Promise<void> {
|
||||
try {
|
||||
const targetBucketName = optionsArg.targetBucket ? optionsArg.targetBucket.name : this.name;
|
||||
|
||||
// Retrieve current object information to use in copy conditions
|
||||
const currentObjInfo = await this.smartbucketRef.minioClient.statObject(bucket, objectKey);
|
||||
|
||||
const currentObjInfo = await this.smartbucketRef.minioClient.statObject(
|
||||
targetBucketName,
|
||||
optionsArg.objectKey
|
||||
);
|
||||
|
||||
// Setting up copy conditions
|
||||
const copyConditions = new plugins.minio.CopyConditions();
|
||||
|
||||
// Prepare new metadata, merging current and new metadata
|
||||
const newMetadata = {
|
||||
...currentObjInfo.metaData,
|
||||
...metadata,
|
||||
|
||||
// Prepare new metadata
|
||||
const newNativeMetadata = {
|
||||
...(optionsArg.deleteExistingNativeMetadata ? {} : currentObjInfo.metaData),
|
||||
...optionsArg.nativeMetadata,
|
||||
};
|
||||
|
||||
|
||||
// Define the copy operation as a Promise
|
||||
// TODO: check on issue here: https://github.com/minio/minio-js/issues/1286
|
||||
await this.smartbucketRef.minioClient.copyObject(
|
||||
bucket,
|
||||
objectKey,
|
||||
`/${bucket}/${objectKey}`,
|
||||
copyConditions,
|
||||
this.name,
|
||||
optionsArg.objectKey,
|
||||
`/${targetBucketName}/${optionsArg.objectKey}`,
|
||||
copyConditions
|
||||
);
|
||||
} catch (err) {
|
||||
console.error('Error updating metadata:', err);
|
||||
@ -171,7 +192,27 @@ export class Bucket {
|
||||
/**
|
||||
* removeObject
|
||||
*/
|
||||
public async fastRemove(pathArg: string) {
|
||||
await this.smartbucketRef.minioClient.removeObject(this.name, pathArg);
|
||||
public async fastRemove(optionsArg: {
|
||||
path: string;
|
||||
}) {
|
||||
await this.smartbucketRef.minioClient.removeObject(this.name, optionsArg.path);
|
||||
}
|
||||
|
||||
public async doesObjectExist(optionsArg: {
|
||||
path: string;
|
||||
}): Promise<boolean> {
|
||||
try {
|
||||
await this.smartbucketRef.minioClient.statObject(this.name, optionsArg.path);
|
||||
console.log(`Object '${optionsArg.path}' exists in bucket '${this.name}'.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error.code === 'NotFound') {
|
||||
console.log(`Object '${optionsArg.path}' does not exist in bucket '${this.name}'.`);
|
||||
return false;
|
||||
} else {
|
||||
console.error('Error checking object existence:', error);
|
||||
throw error; // Rethrow if it's not a NotFound error to handle unexpected issues
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,8 +70,9 @@ export class Directory {
|
||||
false
|
||||
);
|
||||
const fileArray: File[] = [];
|
||||
const duplexStream = plugins.smartstream.createDuplexStream<plugins.minio.BucketItem, void>(
|
||||
async (bucketItem) => {
|
||||
const duplexStream = new plugins.smartstream.SmartDuplex<plugins.minio.BucketItem, void>({
|
||||
objectMode: true,
|
||||
writeFunction: async (bucketItem) => {
|
||||
if (bucketItem.prefix) {
|
||||
return;
|
||||
}
|
||||
@ -83,13 +84,18 @@ export class Directory {
|
||||
subtractedPath = subtractedPath.substr(1);
|
||||
}
|
||||
if (!subtractedPath.includes('/')) {
|
||||
fileArray.push(new File(this, subtractedPath));
|
||||
fileArray.push(
|
||||
new File({
|
||||
directoryRefArg: this,
|
||||
fileName: subtractedPath,
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
async (tools) => {
|
||||
finalFunction: async (tools) => {
|
||||
done.resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
fileNameStream.pipe(duplexStream);
|
||||
await done.promise;
|
||||
return fileArray;
|
||||
@ -107,8 +113,9 @@ export class Directory {
|
||||
false
|
||||
);
|
||||
const directoryArray: Directory[] = [];
|
||||
const duplexStream = plugins.smartstream.createDuplexStream<plugins.minio.BucketItem, void>(
|
||||
async (bucketItem) => {
|
||||
const duplexStream = new plugins.smartstream.SmartDuplex<plugins.minio.BucketItem, void>({
|
||||
objectMode: true,
|
||||
writeFunction: async (bucketItem) => {
|
||||
if (bucketItem.name) {
|
||||
return;
|
||||
}
|
||||
@ -124,10 +131,10 @@ export class Directory {
|
||||
directoryArray.push(new Directory(this.bucketRef, this, dirName));
|
||||
}
|
||||
},
|
||||
async (tools) => {
|
||||
finalFunction: async (tools) => {
|
||||
done.resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
completeDirStream.pipe(duplexStream);
|
||||
await done.promise;
|
||||
return directoryArray;
|
||||
@ -177,36 +184,49 @@ export class Directory {
|
||||
* @param relativePathArg
|
||||
*/
|
||||
public async createEmptyFile(relativePathArg: string) {
|
||||
const emtpyFile = await File.createFileFromString(this, relativePathArg, '');
|
||||
const emtpyFile = await File.create({
|
||||
directory: this,
|
||||
name: relativePathArg,
|
||||
contents: '',
|
||||
});
|
||||
}
|
||||
|
||||
// file operations
|
||||
public async fastStore(pathArg: string, contentArg: string | Buffer) {
|
||||
const path = plugins.path.join(this.getBasePath(), pathArg);
|
||||
await this.bucketRef.fastPut(path, contentArg);
|
||||
public async fastPut(optionsArg: { path: string; contents: string | Buffer }) {
|
||||
const path = plugins.path.join(this.getBasePath(), optionsArg.path);
|
||||
await this.bucketRef.fastPut({
|
||||
path,
|
||||
contents: optionsArg.contents,
|
||||
});
|
||||
}
|
||||
|
||||
public async fastGet(pathArg: string) {
|
||||
const path = plugins.path.join(this.getBasePath(), pathArg);
|
||||
const result = await this.bucketRef.fastGet(path);
|
||||
public async fastGet(optionsArg: { path: string }) {
|
||||
const path = plugins.path.join(this.getBasePath(), optionsArg.path);
|
||||
const result = await this.bucketRef.fastGet({
|
||||
path,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public async fastGetStream(pathArg: string): Promise<plugins.smartrx.rxjs.ReplaySubject<Buffer>> {
|
||||
const path = plugins.path.join(this.getBasePath(), pathArg);
|
||||
const result = await this.bucketRef.fastGetStream(path);
|
||||
const result = await this.bucketRef.fastGetStream({
|
||||
path,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public async fastRemove(pathArg: string) {
|
||||
const path = plugins.path.join(this.getBasePath(), pathArg);
|
||||
await this.bucketRef.fastRemove(path);
|
||||
public async fastRemove(optionsArg: { path: string }) {
|
||||
const path = plugins.path.join(this.getBasePath(), optionsArg.path);
|
||||
await this.bucketRef.fastRemove({
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes the directory with all its contents
|
||||
*/
|
||||
public async deleteWithAllContents() {
|
||||
public async delete() {
|
||||
const deleteDirectory = async (directoryArg: Directory) => {
|
||||
const childDirectories = await directoryArg.listDirectories();
|
||||
if (childDirectories.length === 0) {
|
||||
@ -218,7 +238,9 @@ export class Directory {
|
||||
}
|
||||
const files = await directoryArg.listFiles();
|
||||
for (const file of files) {
|
||||
await directoryArg.fastRemove(file.name);
|
||||
await directoryArg.fastRemove({
|
||||
path: file.name,
|
||||
});
|
||||
}
|
||||
};
|
||||
await deleteDirectory(this);
|
||||
|
@ -7,29 +7,41 @@ export interface IFileMetaData {
|
||||
size: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* represents a file in a directory
|
||||
*/
|
||||
export class File {
|
||||
// STATIC
|
||||
public static async createFileFromString(
|
||||
dirArg: Directory,
|
||||
fileName: string,
|
||||
fileContent: string
|
||||
) {
|
||||
await this.createFileFromBuffer(dirArg, fileName, Buffer.from(fileContent));
|
||||
}
|
||||
|
||||
public static async createFileFromBuffer(
|
||||
directoryRef: Directory,
|
||||
fileName: string,
|
||||
fileContent: Buffer
|
||||
) {
|
||||
const filePath = plugins.path.join(directoryRef.getBasePath(), fileName);
|
||||
const streamIntake = new plugins.smartstream.StreamIntake();
|
||||
const putPromise = directoryRef.bucketRef.smartbucketRef.minioClient
|
||||
.putObject(this.name, filePath, streamIntake.getReadable())
|
||||
.catch((e) => console.log(e));
|
||||
streamIntake.pushData(fileContent);
|
||||
streamIntake.signalEnd();
|
||||
await putPromise;
|
||||
/**
|
||||
* 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
|
||||
@ -39,9 +51,9 @@ export class File {
|
||||
public path: string;
|
||||
public metaData: IFileMetaData;
|
||||
|
||||
constructor(directoryRefArg: Directory, fileName: string) {
|
||||
this.parentDirectoryRef = directoryRefArg;
|
||||
this.name = fileName;
|
||||
constructor(optionsArg: { directoryRefArg: Directory; fileName: string }) {
|
||||
this.parentDirectoryRef = optionsArg.directoryRefArg;
|
||||
this.name = optionsArg.fileName;
|
||||
}
|
||||
|
||||
public async getContentAsString() {
|
||||
@ -55,14 +67,16 @@ export class File {
|
||||
.getObject(this.parentDirectoryRef.bucketRef.name, this.path)
|
||||
.catch((e) => console.log(e));
|
||||
let completeFile = Buffer.from('');
|
||||
const duplexStream = plugins.smartstream.createDuplexStream<Buffer, Buffer>(
|
||||
async (chunk) => {
|
||||
completeFile = Buffer.concat([chunk]);
|
||||
return chunk;
|
||||
},
|
||||
async (cb) => {
|
||||
done.resolve();
|
||||
return 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('');
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
@ -75,7 +89,7 @@ export class File {
|
||||
return completeFile;
|
||||
}
|
||||
|
||||
public async streamContent() {
|
||||
public async readStreaming() {
|
||||
// TODO
|
||||
throw new Error('not yet implemented');
|
||||
}
|
||||
@ -90,4 +104,37 @@ export class File {
|
||||
);
|
||||
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) {}
|
||||
}
|
||||
|
Reference in New Issue
Block a user