@memberjunction/storage 2.43.0 → 2.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/readme.md +315 -36
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/storage",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.44.0",
|
|
4
4
|
"description": "This library provides a set of objects that handle the interface between the server-side API and various cloud storage providers.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"@azure/identity": "^4.0.1",
|
|
26
26
|
"@azure/storage-blob": "^12.17.0",
|
|
27
27
|
"@google-cloud/storage": "^7.9.0",
|
|
28
|
-
"@memberjunction/core": "2.
|
|
29
|
-
"@memberjunction/core-entities": "2.
|
|
30
|
-
"@memberjunction/global": "2.
|
|
28
|
+
"@memberjunction/core": "2.44.0",
|
|
29
|
+
"@memberjunction/core-entities": "2.44.0",
|
|
30
|
+
"@memberjunction/global": "2.44.0",
|
|
31
31
|
"@microsoft/mgt-element": "^3.1.3",
|
|
32
32
|
"@microsoft/mgt-msal2-provider": "^3.1.3",
|
|
33
33
|
"@microsoft/mgt-sharepoint-provider": "^3.1.3",
|
package/readme.md
CHANGED
|
@@ -4,10 +4,18 @@ The `@memberjunction/storage` library provides a unified interface for interacti
|
|
|
4
4
|
|
|
5
5
|
[](https://memberjunction.com)
|
|
6
6
|
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
This library is a key component of the MemberJunction platform, providing seamless file storage operations across multiple cloud providers. It offers a provider-agnostic approach to file management, allowing applications to switch between storage providers without code changes.
|
|
10
|
+
|
|
7
11
|
## Features
|
|
8
12
|
|
|
9
|
-
- **Unified API**: Consistent methods across all storage providers
|
|
13
|
+
- **Unified API**: Consistent methods across all storage providers via the `FileStorageBase` abstract class
|
|
14
|
+
- **Type-Safe**: Full TypeScript support with comprehensive type definitions
|
|
10
15
|
- **Flexible Provider Selection**: Use any number of storage providers simultaneously based on your application needs
|
|
16
|
+
- **Pre-authenticated URLs**: Secure upload and download operations using time-limited URLs
|
|
17
|
+
- **Metadata Support**: Store and retrieve custom metadata with your files
|
|
18
|
+
- **Error Handling**: Provider-specific errors are normalized with clear error messages
|
|
11
19
|
- **Commonly Supported Storage Providers**:
|
|
12
20
|
- [AWS S3](https://aws.amazon.com/s3/)
|
|
13
21
|
- [Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs)
|
|
@@ -21,11 +29,12 @@ The `@memberjunction/storage` library provides a unified interface for interacti
|
|
|
21
29
|
- Download files (via pre-authenticated URLs)
|
|
22
30
|
- Copy and move files
|
|
23
31
|
- Delete files and directories
|
|
24
|
-
- List files and directories
|
|
32
|
+
- List files and directories with metadata
|
|
25
33
|
- Create and manage directories
|
|
26
|
-
- Get file metadata
|
|
34
|
+
- Get detailed file metadata
|
|
27
35
|
- Check file/directory existence
|
|
28
|
-
-
|
|
36
|
+
- Direct upload/download via Buffer
|
|
37
|
+
- **Extensible**: Easy to add new storage providers by extending `FileStorageBase`
|
|
29
38
|
|
|
30
39
|
## Installation
|
|
31
40
|
|
|
@@ -33,6 +42,14 @@ The `@memberjunction/storage` library provides a unified interface for interacti
|
|
|
33
42
|
npm install @memberjunction/storage
|
|
34
43
|
```
|
|
35
44
|
|
|
45
|
+
## Dependencies
|
|
46
|
+
|
|
47
|
+
This package depends on:
|
|
48
|
+
- `@memberjunction/core` - Core MemberJunction functionality
|
|
49
|
+
- `@memberjunction/core-entities` - Entity definitions including `FileStorageProviderEntity`
|
|
50
|
+
- `@memberjunction/global` - Global utilities and class registration
|
|
51
|
+
- Provider-specific SDKs (installed as dependencies)
|
|
52
|
+
|
|
36
53
|
## Usage
|
|
37
54
|
|
|
38
55
|
### Basic Setup
|
|
@@ -48,83 +65,225 @@ STORAGE_AZURE_ACCOUNT_NAME=your-account-name
|
|
|
48
65
|
STORAGE_AZURE_ACCOUNT_KEY=your-account-key
|
|
49
66
|
```
|
|
50
67
|
|
|
51
|
-
###
|
|
68
|
+
### Using Utility Functions (Recommended)
|
|
69
|
+
|
|
70
|
+
The library provides high-level utility functions that work with MemberJunction's entity system:
|
|
52
71
|
|
|
53
72
|
```typescript
|
|
54
|
-
import {
|
|
73
|
+
import { createUploadUrl, createDownloadUrl, deleteObject, moveObject } from '@memberjunction/storage';
|
|
55
74
|
import { FileStorageProviderEntity } from '@memberjunction/core-entities';
|
|
75
|
+
import { Metadata } from '@memberjunction/core';
|
|
56
76
|
|
|
57
|
-
//
|
|
58
|
-
async function fileOperationsExample(
|
|
77
|
+
// Load a FileStorageProviderEntity from the database
|
|
78
|
+
async function fileOperationsExample() {
|
|
79
|
+
const md = new Metadata();
|
|
80
|
+
const provider = await md.GetEntityObject<FileStorageProviderEntity>('File Storage Providers');
|
|
81
|
+
await provider.Load('your-provider-id');
|
|
82
|
+
|
|
59
83
|
// Create pre-authenticated upload URL
|
|
60
84
|
const { updatedInput, UploadUrl } = await createUploadUrl(
|
|
61
85
|
provider,
|
|
62
86
|
{
|
|
63
87
|
ID: '123',
|
|
64
|
-
Name: '
|
|
88
|
+
Name: 'documents/report.pdf',
|
|
65
89
|
ProviderID: provider.ID
|
|
66
90
|
}
|
|
67
91
|
);
|
|
68
92
|
|
|
69
93
|
// The client can use the UploadUrl directly to upload the file
|
|
70
94
|
console.log(`Upload URL: ${UploadUrl}`);
|
|
95
|
+
console.log(`File status: ${updatedInput.Status}`); // 'Uploading'
|
|
96
|
+
console.log(`Content type: ${updatedInput.ContentType}`); // 'application/pdf'
|
|
97
|
+
|
|
98
|
+
// If a ProviderKey was returned, use it for future operations
|
|
99
|
+
const fileIdentifier = updatedInput.ProviderKey || updatedInput.Name;
|
|
71
100
|
|
|
72
101
|
// Later, create pre-authenticated download URL
|
|
73
|
-
const downloadUrl = await createDownloadUrl(provider,
|
|
102
|
+
const downloadUrl = await createDownloadUrl(provider, fileIdentifier);
|
|
74
103
|
console.log(`Download URL: ${downloadUrl}`);
|
|
75
104
|
|
|
105
|
+
// Move the file to a new location
|
|
106
|
+
const moved = await moveObject(
|
|
107
|
+
provider,
|
|
108
|
+
fileIdentifier,
|
|
109
|
+
'documents/archived/report_2024.pdf'
|
|
110
|
+
);
|
|
111
|
+
console.log(`File moved: ${moved}`);
|
|
112
|
+
|
|
76
113
|
// Delete the file when no longer needed
|
|
77
|
-
const deleted = await deleteObject(provider,
|
|
114
|
+
const deleted = await deleteObject(provider, 'documents/archived/report_2024.pdf');
|
|
78
115
|
console.log(`File deleted: ${deleted}`);
|
|
79
116
|
}
|
|
80
117
|
```
|
|
81
118
|
|
|
82
119
|
### Direct Provider Usage
|
|
83
120
|
|
|
84
|
-
You can also work directly with a storage provider:
|
|
121
|
+
You can also work directly with a storage provider by instantiating it using the MemberJunction class factory:
|
|
85
122
|
|
|
86
123
|
```typescript
|
|
87
|
-
import {
|
|
124
|
+
import { FileStorageBase } from '@memberjunction/storage';
|
|
125
|
+
import { MJGlobal } from '@memberjunction/global';
|
|
88
126
|
|
|
89
127
|
async function directProviderExample() {
|
|
90
|
-
|
|
128
|
+
// Create storage instance using the class factory (recommended)
|
|
129
|
+
const storage = MJGlobal.Instance.ClassFactory.CreateInstance<FileStorageBase>(
|
|
130
|
+
FileStorageBase,
|
|
131
|
+
'Azure Blob Storage'
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Initialize the storage provider if needed
|
|
135
|
+
await storage.initialize();
|
|
91
136
|
|
|
92
137
|
// List all files in a directory
|
|
93
138
|
const result = await storage.ListObjects('documents/');
|
|
94
139
|
console.log('Files:', result.objects);
|
|
95
140
|
console.log('Directories:', result.prefixes);
|
|
96
141
|
|
|
142
|
+
// Display detailed metadata for each file
|
|
143
|
+
for (const file of result.objects) {
|
|
144
|
+
console.log(`\nFile: ${file.name}`);
|
|
145
|
+
console.log(` Path: ${file.path}`);
|
|
146
|
+
console.log(` Full Path: ${file.fullPath}`);
|
|
147
|
+
console.log(` Size: ${file.size} bytes`);
|
|
148
|
+
console.log(` Type: ${file.contentType}`);
|
|
149
|
+
console.log(` Modified: ${file.lastModified}`);
|
|
150
|
+
console.log(` Is Directory: ${file.isDirectory}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
97
153
|
// Create a directory
|
|
98
|
-
await storage.CreateDirectory('documents/reports/');
|
|
154
|
+
const dirCreated = await storage.CreateDirectory('documents/reports/');
|
|
155
|
+
console.log(`Directory created: ${dirCreated}`);
|
|
99
156
|
|
|
100
|
-
// Upload a file directly
|
|
157
|
+
// Upload a file directly with metadata
|
|
101
158
|
const content = Buffer.from('Hello, World!');
|
|
102
|
-
await storage.PutObject(
|
|
159
|
+
const uploaded = await storage.PutObject(
|
|
160
|
+
'documents/reports/hello.txt',
|
|
161
|
+
content,
|
|
162
|
+
'text/plain',
|
|
163
|
+
{
|
|
164
|
+
author: 'John Doe',
|
|
165
|
+
department: 'Engineering',
|
|
166
|
+
version: '1.0'
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
console.log(`File uploaded: ${uploaded}`);
|
|
170
|
+
|
|
171
|
+
// Get file metadata without downloading content
|
|
172
|
+
const metadata = await storage.GetObjectMetadata('documents/reports/hello.txt');
|
|
173
|
+
console.log('File metadata:', metadata);
|
|
174
|
+
|
|
175
|
+
// Download file content
|
|
176
|
+
const fileContent = await storage.GetObject('documents/reports/hello.txt');
|
|
177
|
+
console.log('File content:', fileContent.toString('utf8'));
|
|
103
178
|
|
|
104
179
|
// Copy a file
|
|
105
|
-
await storage.CopyObject(
|
|
180
|
+
const copied = await storage.CopyObject(
|
|
181
|
+
'documents/reports/hello.txt',
|
|
182
|
+
'documents/archive/hello-backup.txt'
|
|
183
|
+
);
|
|
184
|
+
console.log(`File copied: ${copied}`);
|
|
106
185
|
|
|
107
186
|
// Check if a file exists
|
|
108
187
|
const exists = await storage.ObjectExists('documents/reports/hello.txt');
|
|
188
|
+
console.log(`File exists: ${exists}`);
|
|
189
|
+
|
|
190
|
+
// Check if a directory exists
|
|
191
|
+
const dirExists = await storage.DirectoryExists('documents/reports/');
|
|
192
|
+
console.log(`Directory exists: ${dirExists}`);
|
|
193
|
+
|
|
194
|
+
// Delete a directory and all its contents
|
|
195
|
+
const dirDeleted = await storage.DeleteDirectory('documents/reports/', true);
|
|
196
|
+
console.log(`Directory deleted: ${dirDeleted}`);
|
|
109
197
|
}
|
|
110
198
|
```
|
|
111
199
|
|
|
200
|
+
## API Reference
|
|
201
|
+
|
|
202
|
+
### Core Types
|
|
203
|
+
|
|
204
|
+
#### `CreatePreAuthUploadUrlPayload`
|
|
205
|
+
```typescript
|
|
206
|
+
type CreatePreAuthUploadUrlPayload = {
|
|
207
|
+
UploadUrl: string; // Pre-authenticated URL for upload
|
|
208
|
+
ProviderKey?: string; // Optional provider-specific key
|
|
209
|
+
};
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
#### `StorageObjectMetadata`
|
|
213
|
+
```typescript
|
|
214
|
+
type StorageObjectMetadata = {
|
|
215
|
+
name: string; // Object name (filename)
|
|
216
|
+
path: string; // Directory path
|
|
217
|
+
fullPath: string; // Complete path including name
|
|
218
|
+
size: number; // Size in bytes
|
|
219
|
+
contentType: string; // MIME type
|
|
220
|
+
lastModified: Date; // Last modification date
|
|
221
|
+
isDirectory: boolean; // Whether this is a directory
|
|
222
|
+
etag?: string; // Entity tag for caching
|
|
223
|
+
cacheControl?: string; // Cache control directives
|
|
224
|
+
customMetadata?: Record<string, string>; // Custom metadata
|
|
225
|
+
};
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
#### `StorageListResult`
|
|
229
|
+
```typescript
|
|
230
|
+
type StorageListResult = {
|
|
231
|
+
objects: StorageObjectMetadata[]; // Files found
|
|
232
|
+
prefixes: string[]; // Directories found
|
|
233
|
+
};
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### FileStorageBase Methods
|
|
237
|
+
|
|
238
|
+
All storage providers implement these methods:
|
|
239
|
+
|
|
240
|
+
- `CreatePreAuthUploadUrl(objectName: string): Promise<CreatePreAuthUploadUrlPayload>`
|
|
241
|
+
- `CreatePreAuthDownloadUrl(objectName: string): Promise<string>`
|
|
242
|
+
- `MoveObject(oldObjectName: string, newObjectName: string): Promise<boolean>`
|
|
243
|
+
- `DeleteObject(objectName: string): Promise<boolean>`
|
|
244
|
+
- `ListObjects(prefix: string, delimiter?: string): Promise<StorageListResult>`
|
|
245
|
+
- `CreateDirectory(directoryPath: string): Promise<boolean>`
|
|
246
|
+
- `DeleteDirectory(directoryPath: string, recursive?: boolean): Promise<boolean>`
|
|
247
|
+
- `GetObjectMetadata(objectName: string): Promise<StorageObjectMetadata>`
|
|
248
|
+
- `GetObject(objectName: string): Promise<Buffer>`
|
|
249
|
+
- `PutObject(objectName: string, data: Buffer, contentType?: string, metadata?: Record<string, string>): Promise<boolean>`
|
|
250
|
+
- `CopyObject(sourceObjectName: string, destinationObjectName: string): Promise<boolean>`
|
|
251
|
+
- `ObjectExists(objectName: string): Promise<boolean>`
|
|
252
|
+
- `DirectoryExists(directoryPath: string): Promise<boolean>`
|
|
253
|
+
- `initialize(): Promise<void>` (optional, for async initialization)
|
|
254
|
+
|
|
255
|
+
### Utility Functions
|
|
256
|
+
|
|
257
|
+
- `createUploadUrl<T>(provider: FileStorageProviderEntity, input: T): Promise<{ updatedInput: T & { Status: string; ContentType: string }, UploadUrl: string }>`
|
|
258
|
+
- `createDownloadUrl(provider: FileStorageProviderEntity, providerKeyOrName: string): Promise<string>`
|
|
259
|
+
- `moveObject(provider: FileStorageProviderEntity, oldProviderKeyOrName: string, newProviderKeyOrName: string): Promise<boolean>`
|
|
260
|
+
- `deleteObject(provider: FileStorageProviderEntity, providerKeyOrName: string): Promise<boolean>`
|
|
261
|
+
|
|
112
262
|
## Architecture
|
|
113
263
|
|
|
114
|
-
The library uses a class hierarchy with `FileStorageBase` as the abstract base class that defines the common interface. Each storage provider implements this interface
|
|
264
|
+
The library uses a class hierarchy with `FileStorageBase` as the abstract base class that defines the common interface. Each storage provider implements this interface:
|
|
115
265
|
|
|
116
266
|
```
|
|
117
267
|
FileStorageBase (Abstract Base Class)
|
|
118
|
-
├── AWSFileStorage
|
|
119
|
-
├── AzureFileStorage
|
|
120
|
-
├── GoogleFileStorage
|
|
121
|
-
├── GoogleDriveFileStorage
|
|
122
|
-
├── SharePointFileStorage
|
|
123
|
-
├── DropboxFileStorage
|
|
124
|
-
└── BoxFileStorage
|
|
268
|
+
├── AWSFileStorage (@RegisterClass: 'AWS S3')
|
|
269
|
+
├── AzureFileStorage (@RegisterClass: 'Azure Blob Storage')
|
|
270
|
+
├── GoogleFileStorage (@RegisterClass: 'Google Cloud Storage')
|
|
271
|
+
├── GoogleDriveFileStorage (@RegisterClass: 'Google Drive')
|
|
272
|
+
├── SharePointFileStorage (@RegisterClass: 'SharePoint')
|
|
273
|
+
├── DropboxFileStorage (@RegisterClass: 'Dropbox')
|
|
274
|
+
└── BoxFileStorage (@RegisterClass: 'Box')
|
|
125
275
|
```
|
|
126
276
|
|
|
127
|
-
|
|
277
|
+
Classes are registered with the MemberJunction global class factory using the `@RegisterClass` decorator, enabling dynamic instantiation based on provider keys.
|
|
278
|
+
|
|
279
|
+
### Integration with MemberJunction
|
|
280
|
+
|
|
281
|
+
This library integrates seamlessly with the MemberJunction platform:
|
|
282
|
+
|
|
283
|
+
1. **Entity System**: Works with `FileStorageProviderEntity` from `@memberjunction/core-entities`
|
|
284
|
+
2. **Class Factory**: Uses `@memberjunction/global` for dynamic provider instantiation
|
|
285
|
+
3. **Configuration**: Provider settings are stored in the MemberJunction database
|
|
286
|
+
4. **Type Safety**: Fully typed interfaces ensure compile-time safety
|
|
128
287
|
|
|
129
288
|
## Storage Provider Configuration
|
|
130
289
|
|
|
@@ -187,22 +346,142 @@ For more information, see [Box Platform Documentation](https://developer.box.com
|
|
|
187
346
|
|
|
188
347
|
## Implementing Additional Providers
|
|
189
348
|
|
|
190
|
-
The library is designed to be extensible.
|
|
349
|
+
The library is designed to be extensible. To add a new storage provider:
|
|
350
|
+
|
|
351
|
+
### 1. Create a New Provider Class
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
import { FileStorageBase, StorageObjectMetadata, StorageListResult } from '@memberjunction/storage';
|
|
355
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
356
|
+
|
|
357
|
+
@RegisterClass(FileStorageBase, 'My Custom Storage')
|
|
358
|
+
export class MyCustomStorage extends FileStorageBase {
|
|
359
|
+
protected readonly providerName = 'My Custom Storage';
|
|
360
|
+
|
|
361
|
+
constructor() {
|
|
362
|
+
super();
|
|
363
|
+
// Initialize your storage client here
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
public async initialize(): Promise<void> {
|
|
367
|
+
// Optional: Perform async initialization
|
|
368
|
+
// e.g., authenticate, verify permissions
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
public async CreatePreAuthUploadUrl(objectName: string): Promise<CreatePreAuthUploadUrlPayload> {
|
|
372
|
+
// Implement upload URL generation
|
|
373
|
+
// Return { UploadUrl: string, ProviderKey?: string }
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
public async CreatePreAuthDownloadUrl(objectName: string): Promise<string> {
|
|
377
|
+
// Implement download URL generation
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Implement all other abstract methods...
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### 2. Handle Unsupported Operations
|
|
385
|
+
|
|
386
|
+
If your provider doesn't support certain operations:
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
public async CreateDirectory(directoryPath: string): Promise<boolean> {
|
|
390
|
+
// If directories aren't supported
|
|
391
|
+
this.throwUnsupportedOperationError('CreateDirectory');
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### 3. Register Environment Variables
|
|
396
|
+
|
|
397
|
+
Document required environment variables:
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
import * as env from 'env-var';
|
|
401
|
+
|
|
402
|
+
constructor() {
|
|
403
|
+
super();
|
|
404
|
+
const apiKey = env.get('STORAGE_MYCUSTOM_API_KEY').required().asString();
|
|
405
|
+
const endpoint = env.get('STORAGE_MYCUSTOM_ENDPOINT').required().asString();
|
|
406
|
+
// Use these to initialize your client
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### 4. Export from Index
|
|
411
|
+
|
|
412
|
+
Add to `src/index.ts`:
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
export * from './drivers/MyCustomStorage';
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### 5. Add to Documentation
|
|
419
|
+
|
|
420
|
+
Update this README with configuration requirements and any provider-specific notes.
|
|
421
|
+
|
|
422
|
+
## Error Handling
|
|
423
|
+
|
|
424
|
+
The library provides consistent error handling across all providers:
|
|
425
|
+
|
|
426
|
+
### UnsupportedOperationError
|
|
427
|
+
|
|
428
|
+
Thrown when a provider doesn't support a specific operation:
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
try {
|
|
432
|
+
await storage.CreateDirectory('/some/path/');
|
|
433
|
+
} catch (error) {
|
|
434
|
+
if (error instanceof UnsupportedOperationError) {
|
|
435
|
+
console.log(`Provider doesn't support directories: ${error.message}`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Provider-Specific Errors
|
|
441
|
+
|
|
442
|
+
Each provider may throw errors specific to its underlying SDK. These are not wrapped, allowing you to handle provider-specific error conditions:
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
try {
|
|
446
|
+
await storage.GetObject('non-existent-file.txt');
|
|
447
|
+
} catch (error) {
|
|
448
|
+
// Handle provider-specific errors
|
|
449
|
+
if (error.code === 'NoSuchKey') { // AWS S3
|
|
450
|
+
console.log('File not found');
|
|
451
|
+
} else if (error.code === 'BlobNotFound') { // Azure
|
|
452
|
+
console.log('Blob not found');
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
## Best Practices
|
|
458
|
+
|
|
459
|
+
1. **Use ProviderKey**: Always check for and use `ProviderKey` if returned by `CreatePreAuthUploadUrl`
|
|
460
|
+
2. **Error Handling**: Implement proper error handling for both generic and provider-specific errors
|
|
461
|
+
3. **Environment Variables**: Store sensitive credentials securely and never commit them to version control
|
|
462
|
+
4. **Content Types**: Always specify content types for better browser handling and security
|
|
463
|
+
5. **Metadata**: Use custom metadata to store additional information without modifying file content
|
|
464
|
+
6. **Directory Paths**: Always use trailing slashes for directory paths (e.g., `documents/` not `documents`)
|
|
465
|
+
7. **Initialize Providers**: Call `initialize()` on providers that require async setup
|
|
191
466
|
|
|
192
|
-
|
|
193
|
-
2. Implementing all required abstract methods
|
|
194
|
-
3. Registering the class using the `@RegisterClass` decorator from `@memberjunction/global`
|
|
467
|
+
## Performance Considerations
|
|
195
468
|
|
|
196
|
-
|
|
469
|
+
- **Pre-authenticated URLs**: Use these for client uploads/downloads to reduce server load
|
|
470
|
+
- **Buffering**: The `GetObject` and `PutObject` methods load entire files into memory; for large files, consider streaming approaches
|
|
471
|
+
- **List Operations**: Use appropriate prefixes and delimiters to limit results
|
|
472
|
+
- **Caching**: Utilize ETags and cache control headers when available
|
|
197
473
|
|
|
198
474
|
## Contributing
|
|
199
475
|
|
|
200
476
|
Contributions are welcome! To add a new storage provider:
|
|
201
477
|
|
|
202
|
-
1.
|
|
203
|
-
2.
|
|
204
|
-
3.
|
|
205
|
-
4.
|
|
478
|
+
1. Fork the repository
|
|
479
|
+
2. Create a feature branch (`git checkout -b feature/new-provider`)
|
|
480
|
+
3. Create your provider class in `src/drivers/`
|
|
481
|
+
4. Implement all required methods from `FileStorageBase`
|
|
482
|
+
5. Add comprehensive tests
|
|
483
|
+
6. Update documentation
|
|
484
|
+
7. Submit a pull request
|
|
206
485
|
|
|
207
486
|
## License
|
|
208
487
|
|