express-storage
Version:
A simple and powerful file upload and storage management package for Express.js applications. Supports multiple storage drivers including S3, GCS, OCI, and local storage with presigned URL support.
491 lines (385 loc) โข 13.3 kB
Markdown
# Express Storage
A powerful, TypeScript-first file upload and storage management package for Express.js applications. Supports multiple cloud storage providers with a simple, unified API.
## ๐ Features
- **TypeScript First**: Fully written in TypeScript with complete type definitions
- **Multiple Storage Drivers**: Support for AWS S3, Google Cloud Storage, Oracle Cloud Infrastructure, and local storage
- **Presigned URLs**: Generate secure, time-limited URLs for direct client uploads
- **Flexible File Handling**: Support for single and multiple file uploads
- **Automatic File Organization**: Files stored in month/year directories for local storage
- **Unique File Naming**: Unix timestamp-based unique filenames with sanitization
- **Environment-based Configuration**: Simple setup using environment variables
- **Class-based API**: Clean, object-oriented interface with `StorageManager`
- **Comprehensive Testing**: Full test coverage with Jest
- **Error Handling**: Consistent error responses with detailed messages
## ๐ฆ Installation
```bash
npm install express-storage
```
## ๐ง Quick Setup
### 1. Environment Configuration
Create a `.env` file in your project root:
```env
# Required: Choose your storage driver
FILE_DRIVER=local
# For local storage (optional - defaults to public/express-storage)
LOCAL_PATH=public/uploads
# For cloud storage (AWS S3 example)
FILE_DRIVER=s3
BUCKET_NAME=my-bucket
AWS_REGION=us-east-1
AWS_ACCESS_KEY=your-access-key
AWS_SECRET_KEY=your-secret-key
# Optional: Presigned URL expiry (default: 600 seconds / 10 minutes)
PRESIGNED_URL_EXPIRY=600
```
### 2. Basic Usage
```typescript
import express from 'express';
import multer from 'multer';
import { StorageManager } from 'express-storage';
const app = express();
const upload = multer();
// Initialize storage manager
const storage = new StorageManager();
// Single file upload
app.post('/upload', upload.single('file'), async (req, res) => {
try {
const result = await storage.uploadFile(req.file!);
if (result.success) {
res.json({
success: true,
fileName: result.fileName,
fileUrl: result.fileUrl
});
} else {
res.status(400).json({ success: false, error: result.error });
}
} catch (error) {
res.status(500).json({ success: false, error: 'Upload failed' });
}
});
// Multiple files upload
app.post('/upload-multiple', upload.array('files', 10), async (req, res) => {
try {
const results = await storage.uploadFiles(req.files as Express.Multer.File[]);
const successful = results.filter(r => r.success);
const failed = results.filter(r => !r.success);
res.json({
success: true,
uploaded: successful.length,
failed: failed.length,
results
});
} catch (error) {
res.status(500).json({ success: false, error: 'Upload failed' });
}
});
```
## ๐๏ธ Supported Storage Drivers
| Driver | Type | Description | Required Environment Variables |
|--------|------|-------------|-------------------------------|
| `local` | Direct | Local file system storage | `LOCAL_PATH` (optional) |
| `s3` | Direct | AWS S3 direct upload | `BUCKET_NAME`, `AWS_REGION`, `AWS_ACCESS_KEY`, `AWS_SECRET_KEY` |
| `s3-presigned` | Presigned | AWS S3 presigned URLs | `BUCKET_NAME`, `AWS_REGION`, `AWS_ACCESS_KEY`, `AWS_SECRET_KEY` |
| `gcs` | Direct | Google Cloud Storage direct upload | `BUCKET_NAME`, `GCS_PROJECT_ID`, `GCS_CREDENTIALS` |
| `gcs-presigned` | Presigned | Google Cloud Storage presigned URLs | `BUCKET_NAME`, `GCS_PROJECT_ID`, `GCS_CREDENTIALS` |
| `oci` | Direct | Oracle Cloud Infrastructure direct upload | `BUCKET_NAME`, `OCI_REGION`, `OCI_CREDENTIALS` |
| `oci-presigned` | Presigned | Oracle Cloud Infrastructure presigned URLs | `BUCKET_NAME`, `OCI_REGION`, `OCI_CREDENTIALS` |
## ๐ Environment Variables Reference
### Core Configuration
- `FILE_DRIVER` (required): Storage driver to use
- `BUCKET_NAME`: Cloud storage bucket name
- `LOCAL_PATH`: Local storage directory path (default: `public/express-storage`)
- `PRESIGNED_URL_EXPIRY`: Presigned URL expiry in seconds (default: 600)
### AWS S3 Configuration
- `AWS_REGION`: AWS region (e.g., `us-east-1`)
- `AWS_ACCESS_KEY`: AWS access key ID
- `AWS_SECRET_KEY`: AWS secret access key
### Google Cloud Storage Configuration
- `GCS_PROJECT_ID`: Google Cloud project ID
- `GCS_CREDENTIALS`: Path to service account JSON file
### Oracle Cloud Infrastructure Configuration
- `OCI_REGION`: OCI region (e.g., `us-ashburn-1`)
- `OCI_CREDENTIALS`: Path to OCI credentials file
## ๐ API Reference
### StorageManager Class
The main class for managing file storage operations.
#### Constructor
```typescript
const storage = new StorageManager();
```
#### Methods
##### File Upload
```typescript
// Single file upload
const result = await storage.uploadFile(file: Express.Multer.File): Promise<FileUploadResult>
// Multiple files upload
const results = await storage.uploadFiles(files: Express.Multer.File[]): Promise<FileUploadResult[]>
// Generic upload (handles both single and multiple)
const result = await storage.upload(input: FileInput): Promise<FileUploadResult | FileUploadResult[]>
```
##### Presigned URLs
```typescript
// Generate upload URL
const result = await storage.generateUploadUrl(fileName: string): Promise<PresignedUrlResult>
// Generate view URL
const result = await storage.generateViewUrl(fileName: string): Promise<PresignedUrlResult>
// Generate multiple upload URLs
const results = await storage.generateUploadUrls(fileNames: string[]): Promise<PresignedUrlResult[]>
// Generate multiple view URLs
const results = await storage.generateViewUrls(fileNames: string[]): Promise<PresignedUrlResult[]>
```
##### File Deletion
```typescript
// Delete single file
const success = await storage.deleteFile(fileName: string): Promise<boolean>
// Delete multiple files
const results = await storage.deleteFiles(fileNames: string[]): Promise<boolean[]>
```
##### Utility Methods
```typescript
// Get current configuration
const config = storage.getConfig(): StorageConfig
// Get driver type
const driverType = storage.getDriverType(): string
// Check if presigned URLs are supported
const isSupported = storage.isPresignedSupported(): boolean
```
### Static Methods
```typescript
// Initialize with custom configuration
const storage = StorageManager.initialize({
driver: 's3',
bucketName: 'my-bucket',
awsRegion: 'us-east-1'
});
// Get available drivers
const drivers = StorageManager.getAvailableDrivers(): string[]
// Clear driver cache
StorageManager.clearCache(): void
```
### Convenience Functions
```typescript
import {
uploadFile,
uploadFiles,
generateUploadUrl,
generateViewUrl,
deleteFile,
deleteFiles,
getStorageManager,
initializeStorageManager
} from 'express-storage';
// Use default storage manager
const result = await uploadFile(file);
const results = await uploadFiles(files);
const urlResult = await generateUploadUrl('filename.jpg');
const success = await deleteFile('filename.jpg');
// Initialize custom storage manager
const storage = initializeStorageManager({
driver: 'local',
localPath: 'uploads'
});
```
## ๐ File Organization
### Local Storage
Files are organized in month/year directories:
```
public/express-storage/
โโโ january/
โ โโโ 2024/
โ โโโ 1703123456_image.jpg
โ โโโ 1703123457_document.pdf
โโโ february/
โ โโโ 2024/
โ โโโ 1705800000_video.mp4
โโโ ...
```
### Cloud Storage
Files are stored with unique timestamps:
```
bucket/
โโโ 1703123456_image.jpg
โโโ 1703123457_document.pdf
โโโ 1705800000_video.mp4
```
## ๐ Presigned URLs
For cloud storage providers, you can generate presigned URLs for secure, direct client uploads:
```typescript
// Generate upload URL for client-side upload
const uploadResult = await storage.generateUploadUrl('my-file.jpg');
if (uploadResult.success) {
// Client can use uploadResult.uploadUrl to upload directly
console.log(uploadResult.uploadUrl);
}
// Generate view URL for secure file access
const viewResult = await storage.generateViewUrl('my-file.jpg');
if (viewResult.success) {
// Client can use viewResult.viewUrl to view the file
console.log(viewResult.viewUrl);
}
```
## ๐ ๏ธ Advanced Usage Examples
### Custom Configuration
```typescript
import { StorageManager } from 'express-storage';
// Initialize with custom config
const storage = StorageManager.initialize({
driver: 's3',
bucketName: 'my-bucket',
awsRegion: 'us-east-1',
awsAccessKey: process.env.AWS_ACCESS_KEY,
awsSecretKey: process.env.AWS_SECRET_KEY,
presignedUrlExpiry: 1800 // 30 minutes
});
```
### Error Handling
```typescript
app.post('/upload', upload.single('file'), async (req, res) => {
try {
const result = await storage.uploadFile(req.file!);
if (result.success) {
res.json({
success: true,
fileName: result.fileName,
fileUrl: result.fileUrl
});
} else {
res.status(400).json({
success: false,
error: result.error
});
}
} catch (error) {
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
});
}
});
```
### File Validation
```typescript
app.post('/upload', upload.single('file'), async (req, res) => {
const file = req.file!;
// Validate file size (5MB limit)
if (file.size > 5 * 1024 * 1024) {
return res.status(400).json({
success: false,
error: 'File size too large. Maximum 5MB allowed.'
});
}
// Validate file type
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (!allowedTypes.includes(file.mimetype)) {
return res.status(400).json({
success: false,
error: 'Invalid file type. Only JPEG, PNG, and GIF allowed.'
});
}
const result = await storage.uploadFile(file);
res.json(result);
});
```
### Multiple File Upload with Progress
```typescript
app.post('/upload-multiple', upload.array('files', 10), async (req, res) => {
const files = req.files as Express.Multer.File[];
const results = await storage.uploadFiles(files);
const summary = {
total: files.length,
successful: results.filter(r => r.success).length,
failed: results.filter(r => !r.success).length,
files: results.map((result, index) => ({
originalName: files[index].originalname,
success: result.success,
fileName: result.fileName,
fileUrl: result.fileUrl,
error: result.error
}))
};
res.json(summary);
});
```
## ๐งช Testing
Run the test suite:
```bash
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage
```
## ๐ ๏ธ Development
### Prerequisites
- Node.js >= 16.0.0
- TypeScript >= 5.1.6
### Development Commands
```bash
# Install dependencies
npm install
# Build the package
npm run build
# Development mode (watch for changes)
npm run dev
# Clean build directory
npm run clean
# Type checking
npm run type-check
# Linting
npm run lint
npm run lint:fix
# Formatting
npm run format
```
### Project Structure
```
express-storage/
โโโ src/
โ โโโ types/
โ โ โโโ storage.types.ts # Type definitions
โ โโโ utils/
โ โ โโโ config.utils.ts # Configuration utilities
โ โ โโโ file.utils.ts # File operation utilities
โ โโโ drivers/
โ โ โโโ base.driver.ts # Abstract base driver
โ โ โโโ local.driver.ts # Local storage driver
โ โ โโโ s3.driver.ts # AWS S3 driver
โ โ โโโ gcs.driver.ts # Google Cloud Storage driver
โ โ โโโ oci.driver.ts # Oracle Cloud Infrastructure driver
โ โโโ factory/
โ โ โโโ driver.factory.ts # Driver factory
โ โโโ storage-manager.ts # Main StorageManager class
โ โโโ index.ts # Package entry point
โโโ tests/ # Test files
โโโ examples/ # Usage examples
โโโ dist/ # Compiled output
โโโ package.json
```
## ๐ค Contributing
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
### Development Guidelines
- Follow TypeScript best practices
- Write comprehensive tests for new features
- Update documentation for API changes
- Ensure all tests pass before submitting PR
## ๐ License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## ๐ Support
- **Issues**: Report bugs and feature requests on GitHub
- **Documentation**: Check the examples folder for usage patterns
- **Questions**: Open a GitHub discussion for questions
## ๐ Changelog
### v1.0.0
- Initial release
- Support for local, S3, GCS, and OCI storage
- Presigned URL generation
- TypeScript-first implementation
- Comprehensive test coverage
---
**Made with โค๏ธ for the Express.js community**