@habibulhasan/nestjs-file-uploader
v0.2.1
Published
A comprehensive file upload module for NestJS with support for multiple storage providers (AWS S3, Digital Ocean Spaces, Local Storage) using Strategy Pattern
Downloads
93
Maintainers
Readme
@habibulhasan/nestjs-file-uploader
A comprehensive, production-ready file upload module for NestJS with support for multiple storage providers using the Strategy Pattern.
Features
✨ Multiple Storage Providers
- Local Filesystem Storage
- AWS S3
- Digital Ocean Spaces (with CDN support)
- Linux Folder (with permissions management)
🎯 Strategy Pattern Implementation
- Easily switch between storage providers
- Runtime storage selection
- Extensible architecture for custom providers
📦 Complete File Management
- Single & bulk file uploads
- Soft & hard delete with restore capability
- File metadata (tags, description, custom fields)
- Folder organization with tree structure
- File search with advanced filters
🔒 Enterprise Features
- File validation (size, MIME type, extensions)
- Virus scanning hooks
- Signed URLs for private files
- Transaction support for bulk operations
- Comprehensive error handling
📊 Advanced Querying
- Pagination, sorting, filtering
- Search by name, MIME type, folder, date range
- Soft-deleted file management
🛠️ Developer Friendly
- Full TypeScript support
- Swagger/OpenAPI documentation
- Dynamic module configuration
- Database migrations included
Installation
npm install @habibulhasan/nestjs-file-uploaderPeer Dependencies
npm install @nestjs/common @nestjs/core @nestjs/platform-express @nestjs/typeorm typeorm reflect-metadata rxjsQuick Start
1. Import the Module
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FileUploadModule } from '@habibulhasan/nestjs-file-uploader';
import { StorageType } from '@habibulhasan/nestjs-file-uploader';
@Module({
imports: [
TypeOrmModule.forRoot({
// your database config
}),
FileUploadModule.forRoot({
storage: {
[StorageType.LOCAL]: {
uploadPath: './uploads',
baseUrl: 'http://localhost:3000/uploads',
},
},
defaultStorageType: StorageType.LOCAL,
}),
],
})
export class AppModule {}2. Run Database Migrations
Copy the migration template from src/migrations/CreateFileAndFolderTables.template.ts to your project's migrations folder and run:
npm run migration:run3. Start Uploading Files
The module automatically registers REST endpoints:
# Upload a single file
POST /files/upload
# Upload multiple files
POST /files/upload/multiple
# List files
GET /files
# Get file by ID
GET /files/:id
# Delete file (soft)
DELETE /files/:id/soft
# Delete file (hard)
DELETE /files/:id/hardConfiguration
Local Storage
FileUploadModule.forRoot({
storage: {
[StorageType.LOCAL]: {
uploadPath: './uploads',
baseUrl: 'http://localhost:3000/uploads',
createFolderIfNotExists: true,
},
},
});AWS S3
FileUploadModule.forRoot({
storage: {
[StorageType.AWS_S3]: {
region: 'us-east-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
bucket: 'my-bucket',
acl: 'public-read', // or 'private'
},
},
});Digital Ocean Spaces
FileUploadModule.forRoot({
storage: {
[StorageType.DIGITAL_OCEAN]: {
region: 'nyc3', // or 'sgp1', 'fra1', etc.
accessKeyId: process.env.DO_SPACES_KEY,
secretAccessKey: process.env.DO_SPACES_SECRET,
bucket: 'my-space',
acl: 'public-read',
cdnEndpoint: 'https://my-cdn.com', // optional CDN
},
},
});Linux Folder (with permissions)
FileUploadModule.forRoot({
storage: {
[StorageType.LINUX_FOLDER]: {
uploadPath: '/var/www/uploads',
baseUrl: 'https://example.com/uploads',
permissions: '0644', // file permissions
owner: { uid: 1000, gid: 1000 }, // optional
},
},
});Multiple Storage Providers
FileUploadModule.forRoot({
storage: {
[StorageType.LOCAL]: { /* config */ },
[StorageType.AWS_S3]: { /* config */ },
[StorageType.DIGITAL_OCEAN]: { /* config */ },
},
defaultStorageType: StorageType.AWS_S3,
validation: {
maxFileSize: 100 * 1024 * 1024, // 100MB
allowedMimeTypes: ['image/jpeg', 'image/png', 'application/pdf', 'video/mp4', 'video/quicktime'],
allowedExtensions: ['jpg', 'png', 'pdf', 'mp4', 'mov', 'avi'],
},
});Video Files & Compression Support
import {
FileUploadModule,
StorageType,
VIDEO_MIME_TYPES,
VIDEO_EXTENSIONS
} from '@habibulhasan/nestjs-file-uploader';
FileUploadModule.forRoot({
storage: {
[StorageType.AWS_S3]: {
region: 'us-east-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
bucket: 'my-video-bucket',
acl: 'public-read',
},
},
validation: {
maxFileSize: 500 * 1024 * 1024, // 500MB for videos
allowedMimeTypes: VIDEO_MIME_TYPES, // Supports MP4, MOV, AVI, WebM, etc.
allowedExtensions: VIDEO_EXTENSIONS,
compression: {
enabled: true,
quality: 23, // Video quality (0-51, lower = better)
videoBitrate: '2M', // 2 Mbps
audioCodec: 'aac',
videoCodec: 'libx264',
format: 'mp4',
},
},
});Supported Video Formats:
- MP4 (
.mp4) - QuickTime (
.mov) - AVI (
.avi) - WebM (
.webm) - MPEG (
.mpeg,.mpg) - Windows Media Video (
.wmv) - Flash Video (
.flv) - 3GPP (
.3gp) - Matroska (
.mkv)
Supported Image Formats:
- JPEG (
.jpg,.jpeg) - PNG (
.png) - GIF (
.gif) - WebP (
.webp) - SVG (
.svg) - BMP (
.bmp) - TIFF (
.tiff)
Other Supported Formats:
- Documents: PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX
- Audio: MP3, M4A, WAV, OGG, FLAC
- Archives: ZIP, RAR, 7Z, TAR, GZ
## Async Configuration
```typescript
FileUploadModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
storage: {
[StorageType.AWS_S3]: {
region: configService.get('AWS_REGION'),
accessKeyId: configService.get('AWS_ACCESS_KEY_ID'),
secretAccessKey: configService.get('AWS_SECRET_ACCESS_KEY'),
bucket: configService.get('AWS_BUCKET'),
},
},
}),
inject: [ConfigService],
});Usage Examples
Upload a File
import { Controller, Post, UseInterceptors, UploadedFile } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { FileService, FileUploadDto, StorageType } from '@habibulhasan/nestjs-file-uploader';
@Controller('upload')
export class UploadController {
constructor(private readonly fileService: FileService) {}
@Post()
@UseInterceptors(FileInterceptor('file'))
async uploadFile(@UploadedFile() file: Express.Multer.File) {
return this.fileService.uploadFile(file, {
storageType: StorageType.AWS_S3,
tags: ['user-avatar'],
isPublic: true,
uploadedBy: 'user-123',
});
}
}List Files with Filters
const files = await this.fileService.findAll({
page: 1,
limit: 20,
search: 'document',
mimetype: 'application/pdf',
tags: ['important'],
minSize: 1024,
maxSize: 1024 * 1024 * 10,
createdAfter: new Date('2024-01-01'),
sortBy: 'createdAt',
sortOrder: 'DESC',
});Bulk Delete Files
const result = await this.fileService.bulkSoftDelete({
fileIds: ['id1', 'id2', 'id3'],
soft: true,
deletedBy: 'admin-123',
});
console.log(`Deleted ${result.successCount} files`);Get Signed URL for Private Files
const url = await this.fileService.getSignedUrl('file-id', 3600); // expires in 1 hourWorking with Folders
import { FolderService } from '@habibulhasan/nestjs-file-uploader';
// Create folder
const folder = await this.folderService.create({
name: 'Documents',
description: 'User documents',
parentId: null, // root level
});
// Get folder tree
const tree = await this.folderService.getTree();
// Move folder
await this.folderService.move(folderId, {
targetParentId: newParentId,
});API Endpoints
Files
POST /files/upload- Upload single filePOST /files/upload/multiple- Upload multiple filesGET /files- List files (with filters)GET /files/:id- Get file by IDGET /files/:id/signed-url- Get signed URLPATCH /files/:id/metadata- Update file metadataDELETE /files/:id/soft- Soft deleteDELETE /files/:id/hard- Hard deletePOST /files/:id/restore- Restore deleted filePOST /files/bulk/delete- Bulk deletePOST /files/bulk/restore- Bulk restorePATCH /files/bulk/metadata- Bulk update metadata
Folders
POST /folders- Create folderGET /folders- List all foldersGET /folders/tree- Get folder treeGET /folders/:id- Get folder by IDGET /folders/:id/stats- Get folder statsGET /folders/:id/children- Get direct childrenGET /folders/:id/descendants- Get all descendantsGET /folders/:id/ancestors- Get ancestors (path to root)PATCH /folders/:id- Update folderPATCH /folders/:id/move- Move folderDELETE /folders/:id/soft- Soft deleteDELETE /folders/:id/hard- Hard delete (requires empty or force)POST /folders/:id/restore- Restore deleted folder
Database Schema
Files Table
id(uuid, primary key)name,originalName,path,urlsize,mimetype,extensionstorageType,storageBucket,storageKeyfolderId(foreign key)uploadedBy,deletedBydescription,tags,metadata(jsonb)isPublic,checksumcreatedAt,updatedAt,deletedAt(soft delete)
Folders Table
id(uuid, primary key)name,description,pathparentId(self-referencing foreign key)fileCount,totalSizemetadata(jsonb)isPubliccreatedAt,updatedAt,deletedAt
Advanced Features
Custom Storage Strategy
import { IStorageStrategy } from '@habibulhasan/nestjs-file-uploader';
class MyCustomStorageStrategy implements IStorageStrategy {
async upload(file, filename, options) {
// Your custom upload logic
}
// ... implement other methods
}
// Register it
storageFactory.registerStrategy(StorageType.CUSTOM, new MyCustomStorageStrategy());Virus Scanning Hook
FileUploadModule.forRoot({
storage: { /* ... */ },
validation: {
virusScanHook: async (file) => {
const isClean = await myVirusScanner.scan(file.buffer);
return isClean; // return true if safe
},
},
});Testing
# Unit tests
npm test
# E2E tests
npm run test:e2e
# Test coverage
npm run test:covLicense
MIT
Contributing
Contributions welcome! Please read our contributing guidelines.
Support
For issues and questions, please use GitHub Issues.
Made with ❤️ by PocketSchool
