npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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

Readme

🚀 Universal Drive Uploader

npm version License: MIT Node.js Version

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

npm install universal-drive-uploader

Requirements: 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 Provider

Never 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 stream
  • options (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


📄 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 stream
  • options (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

  1. Create a Google Cloud Project

  2. Enable Google Drive API

    • Navigate to "APIs & Services" > "Library"
    • Search for "Google Drive API" and enable it
  3. Create OAuth 2.0 Credentials

    • Go to "APIs & Services" > "Credentials"
    • Click "Create Credentials" > "OAuth client ID"
    • Choose "Web application"
    • Add authorized redirect URIs
  4. 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
  5. 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

  1. Register Application in Azure

    • Go to Azure Portal
    • Navigate to "Azure Active Directory" > "App registrations"
    • Click "New registration"
  2. Configure API Permissions

    • Add "Microsoft Graph" permissions
    • Required: Files.ReadWrite, offline_access
    • Grant admin consent
  3. Create Client Secret

    • Go to "Certificates & secrets"
    • Create new client secret
    • Copy the secret value immediately
  4. 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
  5. Get Folder ID (Optional)

    • Use Microsoft Graph API to list folders
    • Or use 'root' for root directory

Zoho WorkDrive Setup

  1. Create Zoho API Console Account

  2. Register Client

    • Click "Add Client"
    • Choose "Server-based Applications"
    • Enter client details and redirect URI
  3. Generate Refresh Token

    • Use authorization code flow
    • Scope: WorkDrive.workspace.ALL
    • Exchange code for refresh token
  4. Get Folder ID

    • Log in to Zoho WorkDrive
    • Navigate to target folder
    • Copy folder ID from URL or use API
  5. Select Data Center

    • Choose based on your Zoho account region
    • Options: US, EU, IN, AU, JP, CN

Box Setup

  1. Create Box Application

    • Go to Box Developer Console
    • Click "Create New App"
    • Choose "Custom App" > "OAuth 2.0 with JWT or Client Credentials"
  2. Configure OAuth 2.0

    • Set redirect URI
    • Add scopes: root_readwrite
  3. Get Client Credentials

    • Copy Client ID and Client Secret
  4. 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
  5. Get Folder ID (Optional)

    • Use '0' for root folder
    • Or get specific folder ID from Box web interface

Amazon S3 Setup

  1. Create AWS Account

  2. Create IAM User

    • Go to IAM > Users > Add user
    • Enable "Programmatic access"
  3. 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/*"
      }]
    }
  4. Get Access Keys

    • Copy Access Key ID and Secret Access Key
  5. 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');
});