universal-drive-uploader
v1.0.0
Published
Production-ready Node.js package providing a unified interface for uploading files to multiple cloud storage providers: Google Drive, OneDrive, Zoho WorkDrive, Box, and Amazon S3. Backend-only with OAuth 2.0 support.
Downloads
11
Maintainers
Readme
🚀 Universal Drive Uploader
Production-ready Node.js package providing a unified interface for uploading files to multiple cloud storage providers.
Upload files to Google Drive, Microsoft OneDrive, Zoho WorkDrive, Box, and Amazon S3 using a single, consistent API.
✨ Features
- 🔐 Secure OAuth 2.0 authentication for Google Drive, OneDrive, Zoho WorkDrive, and Box
- ☁️ AWS IAM credentials support for Amazon S3
- 🎯 Unified API - One interface for all providers
- 📦 Multiple input types - File paths, Buffers, and Streams
- 🔄 Automatic token refresh with intelligent caching
- 📊 Resumable uploads for large files (provider-specific)
- 🌍 Multi-region support for Zoho WorkDrive and S3
- 🛡️ Backend-only - Secure credential handling
- ⚡ Production-ready - Comprehensive error handling and retry logic
- 📝 TypeScript-friendly - Full JSDoc annotations
- ✅ 90%+ test coverage
📋 Table of Contents
- Installation
- Quick Start
- Supported Providers
- Security Architecture
- Configuration
- API Reference
- Provider Setup Guides
- Backend Integration Examples
- Error Handling
- Best Practices
- Troubleshooting
📦 Installation
npm install universal-drive-uploaderRequirements: Node.js 16.0.0 or higher
⚡ Quick Start
Google Drive
const { UniversalDriveUploader } = require('universal-drive-uploader');
const uploader = new UniversalDriveUploader({
provider: 'google-drive',
credentials: {
clientId: 'your_google_client_id',
clientSecret: 'your_google_client_secret',
refreshToken: 'your_google_refresh_token',
folderId: 'your_google_folder_id' // Optional
}
});
const result = await uploader.uploadFile('/path/to/file.pdf');
console.log('✅ Uploaded to Google Drive:', result.fileId);Microsoft OneDrive
const uploader = new UniversalDriveUploader({
provider: 'onedrive',
credentials: {
clientId: 'your_microsoft_client_id',
clientSecret: 'your_microsoft_client_secret',
refreshToken: 'your_microsoft_refresh_token',
folderId: 'your_onedrive_folder_id' // Optional, defaults to 'root'
}
});
const result = await uploader.uploadFile(buffer, { filename: 'document.pdf' });
console.log('✅ Uploaded to OneDrive:', result.fileId);Zoho WorkDrive
const uploader = new UniversalDriveUploader({
provider: 'zoho',
credentials: {
clientId: 'your_zoho_client_id',
clientSecret: 'your_zoho_client_secret',
refreshToken: 'your_zoho_refresh_token',
folderId: 'your_zoho_folder_id', // Required
dataCenter: 'US' // 'US', 'EU', 'IN', 'AU', 'JP', 'CN'
}
});
const result = await uploader.uploadFile(stream, { filename: 'report.pdf' });
console.log('✅ Uploaded to Zoho WorkDrive:', result.fileId);Box
const uploader = new UniversalDriveUploader({
provider: 'box',
credentials: {
clientId: 'your_box_client_id',
clientSecret: 'your_box_client_secret',
refreshToken: 'your_box_refresh_token',
folderId: '0' // Optional, '0' is root folder
}
});
const result = await uploader.uploadFile('/path/to/file.pdf');
console.log('✅ Uploaded to Box:', result.fileId);Amazon S3
const uploader = new UniversalDriveUploader({
provider: 's3',
credentials: {
accessKeyId: 'your_aws_access_key_id',
secretAccessKey: 'your_aws_secret_access_key',
bucket: 'your-bucket-name',
region: 'us-east-1' // Optional, defaults to 'us-east-1'
}
});
const result = await uploader.uploadFile('/path/to/file.pdf', {
prefix: 'uploads', // Optional S3 key prefix
contentType: 'application/pdf',
acl: 'private' // Optional: 'private', 'public-read', etc.
});
console.log('✅ Uploaded to S3:', result.permalink);🌐 Supported Providers
| Provider | OAuth 2.0 | Resumable Uploads | Multi-Region | Status | |----------|-----------|-------------------|--------------|--------| | Google Drive | ✅ | ✅ | ❌ | ✅ Ready | | Microsoft OneDrive | ✅ | ✅ | ❌ | ✅ Ready | | Zoho WorkDrive | ✅ | ❌ | ✅ (6 regions) | ✅ Ready | | Box | ✅ | ✅ | ❌ | ✅ Ready | | Amazon S3 | ❌ (IAM) | ✅ (Multipart) | ✅ | ✅ Ready |
🔐 Security Architecture
⚠️ Backend-Only Package
This package is designed for backend use only. All OAuth credentials (client ID, client secret, refresh tokens) and AWS credentials must remain on the server.
Frontend-to-Backend Proxy Pattern
For frontend applications (React, Vue, Angular), use this secure pattern:
Frontend (Browser) → Backend API → Universal Drive Uploader → Cloud ProviderNever expose credentials to the frontend!
See Backend Integration Examples for implementation details.
⚙️ Configuration
Constructor Options
new UniversalDriveUploader({
provider: string, // Required: 'google-drive', 'onedrive', 'zoho', 'box', 's3'
credentials: object, // Required: Provider-specific credentials
timeout: number, // Optional: Request timeout in ms (default: 30000)
maxFileSize: number // Optional: Max file size in bytes (default: null/unlimited)
})Provider-Specific Credentials
Google Drive, OneDrive, Box
credentials: {
clientId: string, // Required: OAuth client ID
clientSecret: string, // Required: OAuth client secret
refreshToken: string, // Required: OAuth refresh token
folderId: string // Optional: Target folder ID
}Zoho WorkDrive
credentials: {
clientId: string, // Required: OAuth client ID
clientSecret: string, // Required: OAuth client secret
refreshToken: string, // Required: OAuth refresh token
folderId: string, // Required: Parent folder ID
dataCenter: string // Optional: 'US', 'EU', 'IN', 'AU', 'JP', 'CN' (default: 'US')
}Amazon S3
credentials: {
accessKeyId: string, // Required: AWS access key ID
secretAccessKey: string, // Required: AWS secret access key
bucket: string, // Required: S3 bucket name
region: string // Optional: AWS region (default: 'us-east-1')
}📚 API Reference
uploadFile(file, options)
Upload a file to the configured cloud storage provider.
Parameters:
file(string | Buffer | Readable): File path, Buffer, or Readable streamoptions(object): Upload options
Common Options:
{
filename: string, // Required for Buffer/Stream
folderId: string, // Override default folder ID (OAuth providers)
}S3-Specific Options:
{
key: string, // S3 object key (overrides filename)
prefix: string, // S3 key prefix (e.g., 'uploads/')
contentType: string, // MIME type (default: 'application/octet-stream')
metadata: object, // Custom metadata key-value pairs
acl: string, // Access control: 'private', 'public-read', etc.
onProgress: function // Progress callback: (progress) => {}
}Zoho-Specific Options:
{
overrideNameExist: boolean // Override file if name exists (default: false)
}Returns: Promise<Object>
{
success: true,
provider: 'google-drive', // Provider type
fileId: 'abc123', // Provider-specific file ID
filename: 'document.pdf', // File name
size: 12345, // File size in bytes
mimeType: 'application/pdf', // MIME type (if available)
permalink: 'https://...', // Shareable link
downloadUrl: 'https://...', // Direct download URL (if available)
metadata: { ... } // Provider-specific metadata
}Other Methods
getProvider()
Returns the current provider type (e.g., 'google-drive')
getProviderName()
Returns the provider display name (e.g., 'Google Drive')
clearCache()
Clears cached OAuth tokens (forces re-authentication). Not applicable for S3.
static getSupportedProviders()
Returns array of supported provider types
static getProviderNames()
Returns map of provider types to display names
💻 Backend Integration Examples
Express.js with Multer
const express = require('express');
const multer = require('multer');
const { UniversalDriveUploader } = require('universal-drive-uploader');
const app = express();
const upload = multer({ storage: multer.memoryStorage() });
// Initialize uploader
const uploader = new UniversalDriveUploader({
provider: process.env.CLOUD_PROVIDER,
credentials: {
clientId: process.env.OAUTH_CLIENT_ID,
clientSecret: process.env.OAUTH_CLIENT_SECRET,
refreshToken: process.env.OAUTH_REFRESH_TOKEN,
folderId: process.env.FOLDER_ID
}
});
app.post('/api/upload', upload.single('file'), async (req, res) => {
try {
const result = await uploader.uploadFile(req.file.buffer, {
filename: req.file.originalname
});
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3000);Multi-Provider Support
const uploaders = {
'google-drive': new UniversalDriveUploader({
provider: 'google-drive',
credentials: { /* Google credentials */ }
}),
's3': new UniversalDriveUploader({
provider: 's3',
credentials: { /* S3 credentials */ }
})
};
app.post('/api/upload/:provider', upload.single('file'), async (req, res) => {
const uploader = uploaders[req.params.provider];
if (!uploader) {
return res.status(400).json({ error: 'Invalid provider' });
}
const result = await uploader.uploadFile(req.file.buffer, {
filename: req.file.originalname
});
res.json({ success: true, data: result });
});⚠️ Error Handling
Error Classes
const {
UniversalUploaderError, // Base error class
ConfigurationError, // Invalid configuration
AuthenticationError, // OAuth/auth failures
UploadError, // Upload failures
FileInputError, // Invalid file input
ProviderAPIError, // Provider API errors
RateLimitError, // Rate limit exceeded
NetworkError, // Network failures
TimeoutError, // Request timeout
UnsupportedProviderError // Invalid provider
} = require('universal-drive-uploader');Error Handling Example
try {
const result = await uploader.uploadFile(file);
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Authentication failed:', error.message);
// Refresh credentials or notify admin
} else if (error instanceof RateLimitError) {
console.error('Rate limit exceeded. Retry after:', error.retryAfter);
// Implement retry logic
} else if (error instanceof FileInputError) {
console.error('Invalid file:', error.message);
// Notify user about file requirements
} else {
console.error('Upload failed:', error.message);
}
}✅ Best Practices
1. Environment Variables
Never hardcode credentials! Use environment variables:
require('dotenv').config();
const uploader = new UniversalDriveUploader({
provider: process.env.CLOUD_PROVIDER,
credentials: {
clientId: process.env.OAUTH_CLIENT_ID,
clientSecret: process.env.OAUTH_CLIENT_SECRET,
refreshToken: process.env.OAUTH_REFRESH_TOKEN,
folderId: process.env.FOLDER_ID
}
});2. File Validation
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'application/pdf'];
if (file.size > MAX_FILE_SIZE) {
throw new Error('File too large');
}
if (!ALLOWED_TYPES.includes(file.mimetype)) {
throw new Error('File type not allowed');
}3. Error Retry Logic
async function uploadWithRetry(uploader, file, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await uploader.uploadFile(file, options);
} catch (error) {
if (error instanceof RateLimitError && i < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, 2000 * (i + 1)));
continue;
}
throw error;
}
}
}4. Progress Tracking (S3)
const result = await s3Uploader.uploadFile(largeFile, {
filename: 'large-file.zip',
onProgress: (progress) => {
const percent = Math.round((progress.loaded / progress.total) * 100);
console.log(`Upload progress: ${percent}%`);
}
});🔍 Troubleshooting
Authentication Errors
Problem: AuthenticationError: Token refresh failed
Solutions:
- Verify OAuth credentials are correct
- Ensure refresh token hasn't expired or been revoked
- Check data center setting (Zoho)
- Regenerate refresh token if necessary
Upload Failures
Problem: UploadError: Upload failed
Solutions:
- Check file size limits
- Verify folder ID exists and is accessible
- Ensure proper permissions/scopes
- Check network connectivity
Rate Limiting
Problem: RateLimitError: Rate limit exceeded
Solutions:
- Implement exponential backoff retry logic
- Reduce upload frequency
- Use batch uploads where possible
- Check provider-specific rate limits
S3 Permissions
Problem: ProviderAPIError: Access Denied
Solutions:
- Verify IAM user has S3 permissions
- Check bucket policy
- Ensure bucket exists in specified region
- Verify access keys are active
📖 Additional Resources
- Google Drive API Documentation
- Microsoft Graph OneDrive API
- Zoho WorkDrive API
- Box API Documentation
- AWS S3 Documentation
📄 License
MIT © appletosolutions
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📧 Support
For issues and questions:
See Backend Integration Examples for implementation details.
⚙️ Configuration
Constructor Options
new UniversalDriveUploader({
provider: string, // Required: 'google-drive', 'onedrive', 'zoho', 'box', 's3'
credentials: object, // Required: Provider-specific credentials
timeout: number, // Optional: Request timeout in ms (default: 30000)
maxFileSize: number // Optional: Max file size in bytes (default: null/unlimited)
})Provider-Specific Credentials
Google Drive & OneDrive & Box
credentials: {
clientId: string, // Required: OAuth client ID
clientSecret: string, // Required: OAuth client secret
refreshToken: string, // Required: OAuth refresh token
folderId: string // Optional: Target folder ID
}Zoho WorkDrive
credentials: {
clientId: string, // Required: OAuth client ID
clientSecret: string, // Required: OAuth client secret
refreshToken: string, // Required: OAuth refresh token
folderId: string, // Required: Parent folder ID
dataCenter: string // Optional: 'US', 'EU', 'IN', 'AU', 'JP', 'CN' (default: 'US')
}Amazon S3
credentials: {
accessKeyId: string, // Required: AWS access key ID
secretAccessKey: string, // Required: AWS secret access key
bucket: string, // Required: S3 bucket name
region: string // Optional: AWS region (default: 'us-east-1')
}📚 API Reference
uploadFile(file, options)
Upload a file to the configured cloud storage provider.
Parameters:
file(string | Buffer | Readable): File path, Buffer, or Readable streamoptions(object): Upload options
Common Options:
{
filename: string, // Required for Buffer/Stream
folderId: string, // Override default folder ID (OAuth providers)
}S3-Specific Options:
{
key: string, // S3 object key (overrides filename)
prefix: string, // S3 key prefix (e.g., 'uploads/')
contentType: string, // MIME type (default: 'application/octet-stream')
metadata: object, // Custom metadata key-value pairs
acl: string, // Access control: 'private', 'public-read', etc.
onProgress: function // Progress callback: (progress) => {}
}Zoho-Specific Options:
{
overrideNameExist: boolean // Override file if name exists (default: false)
}Returns: Promise
{
success: true,
provider: 'google-drive', // Provider type
fileId: 'abc123', // Provider-specific file ID
filename: 'document.pdf', // File name
size: 12345, // File size in bytes
mimeType: 'application/pdf', // MIME type (if available)
permalink: 'https://...', // Shareable link
downloadUrl: 'https://...', // Direct download URL (if available)
metadata: { ... } // Provider-specific metadata
}getProvider()
Get the current provider type.
Returns: string - Provider type (e.g., 'google-drive')
getProviderName()
Get the current provider display name.
Returns: string - Provider display name (e.g., 'Google Drive')
clearCache()
Clear cached OAuth tokens (forces re-authentication on next request). Not applicable for S3.
Static Methods
UniversalDriveUploader.getSupportedProviders()
Get list of supported provider types.
Returns: string[] - Array of provider types
['google-drive', 'onedrive', 'zoho', 'box', 's3']UniversalDriveUploader.getProviderNames()
Get provider display names.
Returns: Object - Map of provider types to display names
{
'google-drive': 'Google Drive',
'onedrive': 'Microsoft OneDrive',
'zoho': 'Zoho WorkDrive',
'box': 'Box',
's3': 'Amazon S3'
}🔧 Provider Setup Guides
Google Drive Setup
Create a Google Cloud Project
- Go to Google Cloud Console
- Create a new project or select existing
Enable Google Drive API
- Navigate to "APIs & Services" > "Library"
- Search for "Google Drive API" and enable it
Create OAuth 2.0 Credentials
- Go to "APIs & Services" > "Credentials"
- Click "Create Credentials" > "OAuth client ID"
- Choose "Web application"
- Add authorized redirect URIs
Get Refresh Token
- Use OAuth 2.0 Playground or implement authorization flow
- Request scope:
https://www.googleapis.com/auth/drive.file - Exchange authorization code for refresh token
Get Folder ID (Optional)
- Open Google Drive folder in browser
- Copy ID from URL:
https://drive.google.com/drive/folders/FOLDER_ID_HERE
Microsoft OneDrive Setup
Register Application in Azure
- Go to Azure Portal
- Navigate to "Azure Active Directory" > "App registrations"
- Click "New registration"
Configure API Permissions
- Add "Microsoft Graph" permissions
- Required:
Files.ReadWrite,offline_access - Grant admin consent
Create Client Secret
- Go to "Certificates & secrets"
- Create new client secret
- Copy the secret value immediately
Get Refresh Token
- Implement OAuth 2.0 authorization flow
- Authorization endpoint:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize - Token endpoint:
https://login.microsoftonline.com/common/oauth2/v2.0/token
Get Folder ID (Optional)
- Use Microsoft Graph API to list folders
- Or use 'root' for root directory
Zoho WorkDrive Setup
Create Zoho API Console Account
- Go to Zoho API Console
- Sign in with your Zoho account
Register Client
- Click "Add Client"
- Choose "Server-based Applications"
- Enter client details and redirect URI
Generate Refresh Token
- Use authorization code flow
- Scope:
WorkDrive.workspace.ALL - Exchange code for refresh token
Get Folder ID
- Log in to Zoho WorkDrive
- Navigate to target folder
- Copy folder ID from URL or use API
Select Data Center
- Choose based on your Zoho account region
- Options: US, EU, IN, AU, JP, CN
Box Setup
Create Box Application
- Go to Box Developer Console
- Click "Create New App"
- Choose "Custom App" > "OAuth 2.0 with JWT or Client Credentials"
Configure OAuth 2.0
- Set redirect URI
- Add scopes:
root_readwrite
Get Client Credentials
- Copy Client ID and Client Secret
Generate Refresh Token
- Implement OAuth 2.0 flow
- Authorization endpoint:
https://account.box.com/api/oauth2/authorize - Token endpoint:
https://api.box.com/oauth2/token
Get Folder ID (Optional)
- Use '0' for root folder
- Or get specific folder ID from Box web interface
Amazon S3 Setup
Create AWS Account
- Sign up at AWS Console
Create IAM User
- Go to IAM > Users > Add user
- Enable "Programmatic access"
Attach S3 Permissions
- Attach policy:
AmazonS3FullAccess(or create custom policy) - Example custom policy:
{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["s3:PutObject", "s3:GetObject"], "Resource": "arn:aws:s3:::your-bucket-name/*" }] }- Attach policy:
Get Access Keys
- Copy Access Key ID and Secret Access Key
Create S3 Bucket
- Go to S3 > Create bucket
- Choose region and configure settings
💻 Backend Integration Examples
Express.js with Multer
const express = require('express');
const multer = require('multer');
const { UniversalDriveUploader } = require('universal-drive-uploader');
const app = express();
const upload = multer({ storage: multer.memoryStorage() });
// Initialize uploader (use environment variables for credentials)
const uploader = new UniversalDriveUploader({
provider: process.env.CLOUD_PROVIDER, // 'google-drive', 'onedrive', etc.
credentials: {
clientId: process.env.OAUTH_CLIENT_ID,
clientSecret: process.env.OAUTH_CLIENT_SECRET,
refreshToken: process.env.OAUTH_REFRESH_TOKEN,
folderId: process.env.FOLDER_ID
}
});
// Upload endpoint
app.post('/api/upload', upload.single('file'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({ error: 'No file provided' });
}
const result = await uploader.uploadFile(req.file.buffer, {
filename: req.file.originalname
});
res.json({
success: true,
message: 'File uploaded successfully',
data: result
});
} catch (error) {
console.error('Upload error:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});