@atlex/storage
v0.1.5
Published
Filesystem abstraction (local, S3, GCS) for Atlex
Downloads
734
Maintainers
Readme
@atlex/storage
Filesystem abstraction for local, S3, and Google Cloud Storage.
Installation
npm install @atlex/storage
# or
yarn add @atlex/storageQuick Start
import { StorageManager } from '@atlex/storage'
import { LocalDriver } from '@atlex/storage'
const manager = new StorageManager()
manager.disk('local', new LocalDriver({ root: './storage' }))
const disk = manager.disk('local')
// Write file
await disk.put('users/avatar.jpg', fileContent)
// Read file
const content = await disk.get('users/avatar.jpg')
// Check existence
const exists = await disk.exists('users/avatar.jpg')
// Delete file
await disk.delete('users/avatar.jpg')
// Get file URL
const url = disk.url('users/avatar.jpg')Features
- Multi-Driver Support: Local, S3, Google Cloud Storage
- Unified API: Same interface across all storage drivers
- File Operations: Read, write, delete, copy, move files
- Directory Management: Create and delete directories
- Metadata: Get file size, MIME type, visibility, last modified time
- Temporary URLs: Generate temporary signed URLs for secure access
- File Uploads: Seamless handling of multipart file uploads
- Visibility Control: Set public/private file visibility
- Content Streaming: Stream file content for large files
Basic File Operations
Reading Files
import { StorageManager } from '@atlex/storage'
import { LocalDriver } from '@atlex/storage'
const manager = new StorageManager()
const disk = manager.disk('local', new LocalDriver({ root: './storage' }))
// Read entire file as string
const content = await disk.get('path/to/file.txt')
// Read file as bytes/buffer
const buffer = await disk.getBytes('path/to/file.jpg')
// Check if file exists
const exists = await disk.exists('path/to/file.txt')
const missing = await disk.missing('path/to/file.txt')Writing Files
const disk = manager.disk('local')
// Write string content
await disk.put('path/to/file.txt', 'Hello, World!')
// Write buffer content
const buffer = Buffer.from('binary data')
await disk.put('path/to/file.bin', buffer)
// Append to file
await disk.append('logs/app.log', 'Log message\n')
// Write with visibility
await disk.put('public/image.jpg', buffer, { visibility: 'public' })File Management
const disk = manager.disk('local')
// Copy file
await disk.copy('original.txt', 'backup.txt')
// Move file
await disk.move('old/location.txt', 'new/location.txt')
// Delete file
await disk.delete('path/to/file.txt')
// Delete directory (recursively)
await disk.deleteDirectory('path/to/directory')
// Create directory
await disk.makeDirectory('new/directory/path')File Metadata
const disk = manager.disk('local')
// Get file size in bytes
const size = await disk.size('file.txt')
// Get MIME type
const mimeType = await disk.mimeType('image.jpg')
// Get last modification time
const modified = await disk.lastModified('file.txt')
// Get file visibility
const visibility = await disk.getVisibility('file.txt')
// Set file visibility
await disk.setVisibility('file.txt', 'public')Working with URLs
const disk = manager.disk('local')
// Get public URL (if accessible)
const url = disk.url('public/image.jpg')
// Returns: /storage/public/image.jpg
// Generate temporary signed URL (S3/GCS)
const tempUrl = disk.temporaryUrl('private/document.pdf', {
expiresIn: 3600, // 1 hour
})Directory Operations
const disk = manager.disk('local')
// List directory contents
const files = await disk.listContents('uploads')
// Returns: [
// { path: 'uploads/file1.txt', type: 'file', size: 1024 },
// { path: 'uploads/subdir', type: 'dir' }
// ]
// List recursively
const allFiles = await disk.listContents('uploads', { recursive: true })
// Create nested directories
await disk.makeDirectory('uploads/users/profiles')Local Driver
import { LocalDriver } from '@atlex/storage'
import path from 'path'
const driver = new LocalDriver({
root: path.join(__dirname, '../storage'),
public: path.join(__dirname, '../public/storage'),
})
const manager = new StorageManager()
manager.disk('local', driver)S3 Driver
import { S3Driver } from '@atlex/storage'
const driver = new S3Driver({
key: process.env.AWS_ACCESS_KEY_ID,
secret: process.env.AWS_SECRET_ACCESS_KEY,
bucket: process.env.AWS_BUCKET,
region: process.env.AWS_REGION,
endpoint: 'https://s3.amazonaws.com',
})
const manager = new StorageManager()
manager.disk('s3', driver)
const disk = manager.disk('s3')
await disk.put('uploads/file.txt', 'content')
const url = disk.url('uploads/file.txt')
const tempUrl = disk.temporaryUrl('uploads/file.txt', { expiresIn: 3600 })Memory Driver
import { MemoryDriver } from '@atlex/storage'
const driver = new MemoryDriver()
const manager = new StorageManager()
manager.disk('memory', driver)
const disk = manager.disk('memory')
await disk.put('temp/data.json', JSON.stringify({ test: true }))Handling File Uploads
UploadedFile API
import { UploadedFile } from '@atlex/storage'
class UserController {
async uploadAvatar(file: UploadedFile) {
// Get file info
const originalName = file.originalName
const extension = file.extension
const mimeType = file.mimeType
const size = file.size
// Get file stream
const stream = file.stream()
// Get file buffer
const buffer = await file.buffer()
// Store file
const path = `avatars/${Date.now()}.${extension}`
await file.store(path, 'local')
return { path, originalName }
}
}Parsing Multipart Uploads
import { parseUploads } from '@atlex/storage'
import { Request } from '@atlex/http'
async function handleUpload(req: Request) {
const uploads = await parseUploads(req)
const avatar = uploads.file('avatar')
if (avatar) {
const path = await avatar.store('avatars', 'local')
console.log('Saved to:', path)
}
const documents = uploads.files('documents')
for (const doc of documents) {
await doc.store('documents', 's3')
}
}StorageManager Configuration
import { StorageManager } from '@atlex/storage'
import { LocalDriver, S3Driver } from '@atlex/storage'
import { FilesystemsConfig } from '@atlex/storage'
const config: FilesystemsConfig = {
default: 'local',
disks: {
local: {
driver: 'local',
root: './storage',
},
public: {
driver: 'local',
root: './public/storage',
url: '/storage',
},
s3: {
driver: 's3',
key: process.env.AWS_ACCESS_KEY_ID,
secret: process.env.AWS_SECRET_ACCESS_KEY,
region: process.env.AWS_REGION,
bucket: process.env.AWS_BUCKET,
},
},
}
const manager = StorageManager.fromConfig(config)
// Use default disk
const disk = manager.disk()
// Switch disks at runtime
const publicDisk = manager.disk('public')
const s3Disk = manager.disk('s3')
// Replace disk driver
manager.replaceDisk('local', new LocalDriver({ root: './new-storage' }))Complete Example
import { StorageManager } from '@atlex/storage'
import { LocalDriver, S3Driver } from '@atlex/storage'
import path from 'path'
class FileService {
private storage: StorageManager
constructor() {
this.storage = new StorageManager()
this.setupDisks()
}
private setupDisks() {
const env = process.env.NODE_ENV || 'development'
// Local storage for development
this.storage.disk(
'local',
new LocalDriver({
root: path.join(__dirname, '../storage'),
public: path.join(__dirname, '../public/storage'),
}),
)
// S3 for production
if (env === 'production') {
this.storage.disk(
's3',
new S3Driver({
key: process.env.AWS_ACCESS_KEY_ID!,
secret: process.env.AWS_SECRET_ACCESS_KEY!,
bucket: process.env.AWS_BUCKET!,
region: process.env.AWS_REGION!,
}),
)
}
}
async uploadUserAvatar(userId: string, file: UploadedFile) {
const disk = this.storage.disk()
// Validate file
if (!['image/jpeg', 'image/png'].includes(file.mimeType)) {
throw new Error('Only JPEG and PNG images are allowed')
}
if (file.size > 5 * 1024 * 1024) {
throw new Error('File must be less than 5MB')
}
// Generate filename
const filename = `${userId}-${Date.now()}.${file.extension}`
const path = `avatars/${filename}`
// Store file
await disk.put(path, await file.buffer(), {
visibility: 'public',
})
return {
path,
url: disk.url(path),
mimeType: file.mimeType,
size: file.size,
}
}
async deleteOldFiles(directory: string, days: number = 30) {
const disk = this.storage.disk()
const cutoffTime = Date.now() - days * 24 * 60 * 60 * 1000
const files = await disk.listContents(directory, { recursive: true })
for (const file of files) {
if (file.type === 'file') {
const lastModified = await disk.lastModified(file.path)
if (lastModified.getTime() < cutoffTime) {
await disk.delete(file.path)
console.log(`Deleted: ${file.path}`)
}
}
}
}
async getTemporaryAccessUrl(filePath: string, expiresIn: number = 3600) {
const disk = this.storage.disk()
return disk.temporaryUrl(filePath, { expiresIn })
}
}
export default new FileService()API Overview
StorageManager
| Method | Description |
| --------------------------- | --------------------------------- |
| disk(name?) | Get or set a storage disk |
| fromConfig(config) | Create manager from configuration |
| replaceDisk(name, driver) | Replace a disk driver |
Disk
| Method | Description |
| --------------------------------- | ---------------------------- |
| exists(path) | Check if file exists |
| missing(path) | Check if file is missing |
| get(path) | Read file as string |
| getBytes(path) | Read file as buffer |
| put(path, content, options?) | Write file |
| putFile(path, file) | Write from file |
| delete(path) | Delete file |
| deleteDirectory(path) | Delete directory recursively |
| makeDirectory(path) | Create directory |
| append(path, content) | Append to file |
| copy(from, to) | Copy file |
| move(from, to) | Move file |
| getVisibility(path) | Get file visibility |
| setVisibility(path, visibility) | Set file visibility |
| url(path) | Get public URL |
| temporaryUrl(path, options) | Get temporary signed URL |
| lastModified(path) | Get last modification time |
| size(path) | Get file size |
| mimeType(path) | Get MIME type |
| listContents(path, options?) | List directory contents |
Drivers
| Driver | Description |
| -------------- | --------------------------- |
| LocalDriver | File system storage |
| S3Driver | Amazon S3 storage |
| MemoryDriver | In-memory storage (testing) |
Documentation
For complete documentation, visit https://atlex.dev/guide/storage
License
MIT
Part of Atlex — A modern framework for Node.js.
