Skip to content

katungi/expo-azure-blob-storage

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

15 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸš€ Expo Azure Blob Storage

logo

npm version npm downloads license issues

Easy Azure Blob Storage uploads for Expo and React Native applications

A powerful, type-safe library that simplifies Azure Blob Storage integration with built-in Expo ImagePicker support, progress tracking, and comprehensive error handling.


✨ Features

  • πŸ”₯ Easy Integration - Simple setup with Azure Blob Storage
  • πŸ“± Expo ImagePicker Support - Built-in image/camera functionality
  • πŸ“Š Progress Tracking - Real-time upload progress monitoring
  • πŸ”’ SAS Token Management - Secure token handling and refresh
  • πŸ“ Full TypeScript Support - Complete type definitions
  • πŸ”„ Multiple File Uploads - Batch upload with progress tracking
  • ⚑ Framework Agnostic - Core uploader works with any React Native app
  • πŸ›‘οΈ Error Handling - Comprehensive error management
  • 🎯 Permission Management - Automatic permission requests
  • πŸ“± Cross-Platform - Works on iOS, Android, and Web

πŸ“¦ Installation

npm install expo-azure-blob-storage

Peer Dependencies

This package requires the following peer dependencies:

npx expo install expo-file-system expo-image-picker

Manual Installation:

npm install expo-file-system expo-image-picker

πŸš€ Quick Start

Basic File Upload

import { AzureBlobUploader } from 'expo-azure-blob-storage';

const uploader = new AzureBlobUploader({
  storageAccount: 'your-storage-account',
  containerName: 'your-container',
  sasToken: 'your-sas-token'
});

const uploadFile = async (fileUri: string) => {
  const result = await uploader.uploadFile(fileUri, 'my-image.jpg', 'image');
  
  if (result.success) {
    console.log('βœ… Upload successful!');
    console.log('πŸ“‚ File URL:', result.url);
    console.log('πŸ“Š File Size:', result.size, 'bytes');
  } else {
    console.error('❌ Upload failed:', result.error);
  }
};

Expo Image Picker Integration

import { ExpoImageUploader } from 'expo-azure-blob-storage';

const imageUploader = new ExpoImageUploader({
  storageAccount: 'your-storage-account',
  containerName: 'your-container',
  sasToken: 'your-sas-token'
});

const uploadFromLibrary = async () => {
  const result = await imageUploader.quickUploadFromLibrary(
    'my-photo.jpg',
    { quality: 0.8 },
    (progress) => {
      const percent = Math.round((progress.totalBytesWritten / progress.totalBytesExpectedToWrite) * 100);
      console.log(`Upload progress: ${percent}%`);
    }
  );
  
  if (result?.success) {
    console.log('πŸŽ‰ Image uploaded:', result.url);
  }
};

πŸ“š API Reference

AzureBlobUploader

The core uploader class for Azure Blob Storage operations.

Constructor

new AzureBlobUploader(config: AzureBlobConfig)

Methods

uploadFile(fileUri, fileName, mediaType?)

Upload a single file to Azure Blob Storage.

const result = await uploader.uploadFile(
  'file:///path/to/file.jpg',
  'my-image.jpg',
  'image'
);
uploadWithProgress(fileUri, fileName, mediaType?, onProgress?)

Upload a file with progress tracking.

const result = await uploader.uploadWithProgress(
  fileUri,
  'image.jpg',
  'image',
  (progress) => console.log(`${progress.totalBytesWritten}/${progress.totalBytesExpectedToWrite}`)
);
uploadMultipleFiles(files, onProgress?, onFileComplete?)

Upload multiple files with batch progress tracking.

const files = [
  { uri: 'file:///path/1.jpg', name: 'image1.jpg', type: 'image' },
  { uri: 'file:///path/2.jpg', name: 'image2.jpg', type: 'image' }
];

const results = await uploader.uploadMultipleFiles(
  files,
  (fileIndex, progress) => console.log(`File ${fileIndex + 1} progress:`, progress),
  (fileIndex, result) => console.log(`File ${fileIndex + 1} completed:`, result)
);

ExpoImageUploader

Expo-specific image handling built on top of AzureBlobUploader.

Methods

quickUploadFromLibrary(fileName?, options?, onProgress?)

Pick an image from the library and upload in one step.

const result = await imageUploader.quickUploadFromLibrary(
  'profile-pic.jpg',
  { quality: 0.8, allowsEditing: true },
  (progress) => console.log('Progress:', progress)
);
quickUploadFromCamera(fileName?, options?, onProgress?)

Take a photo and upload in one step.

const result = await imageUploader.quickUploadFromCamera(
  'camera-photo.jpg',
  { quality: 0.9, aspect: [16, 9] }
);
pickMultipleImagesFromLibrary(options?)

Pick multiple images from the library.

const pickerResult = await imageUploader.pickMultipleImagesFromLibrary({
  selectionLimit: 5,
  quality: 0.8
});

πŸ”§ Configuration

AzureBlobConfig

interface AzureBlobConfig {
  storageAccount: string;    // Azure storage account name
  containerName: string;     // Blob container name
  sasToken: string;          // SAS token with required permissions
}

ImagePickerOptions

interface ImagePickerOptions {
  quality?: number;                    // Image quality (0-1)
  aspect?: [number, number];           // Aspect ratio [width, height]
  allowsEditing?: boolean;             // Allow editing after selection
  allowsMultipleSelection?: boolean;   // Allow multiple image selection
  selectionLimit?: number;             // Maximum number of images to select
}

🎯 Advanced Usage

Upload with Progress Tracking

const uploadWithProgressBar = async (fileUri: string) => {
  const result = await uploader.uploadWithProgress(
    fileUri,
    'document.pdf',
    'document',
    (progress) => {
      const percentage = Math.round(
        (progress.totalBytesWritten / progress.totalBytesExpectedToWrite) * 100
      );
      
      // Update your UI progress bar
      setUploadProgress(percentage);
      
      console.log(`πŸ“Š Upload Progress: ${percentage}%`);
    }
  );
  
  return result;
};

Multiple File Upload with Error Handling

const uploadMultipleWithErrorHandling = async (files: Array<{uri: string, name: string}>) => {
  const results = await uploader.uploadMultipleFiles(
    files.map(f => ({ ...f, type: 'image' as const })),
    (fileIndex, progress) => {
      console.log(`File ${fileIndex + 1} progress:`, progress);
    },
    (fileIndex, result) => {
      if (result.success) {
        console.log(`βœ… File ${fileIndex + 1} uploaded successfully`);
      } else {
        console.error(`❌ File ${fileIndex + 1} failed:`, result.error);
      }
    }
  );
  
  const successful = results.filter(r => r.success);
  const failed = results.filter(r => !r.success);
  
  console.log(`πŸ“Š Upload Summary: ${successful.length} successful, ${failed.length} failed`);
  
  return { successful, failed };
};

πŸ“± React Native Integration

Complete Upload Component

import React, { useState } from 'react';
import { View, Button, Text, ProgressBarAndroid } from 'react-native';
import { ExpoImageUploader } from 'expo-azure-blob-storage';

const ImageUploadComponent = () => {
  const [uploading, setUploading] = useState(false);
  const [progress, setProgress] = useState(0);
  const [uploadedUrl, setUploadedUrl] = useState<string | null>(null);

  const uploader = new ExpoImageUploader({
    storageAccount: 'your-account',
    containerName: 'your-container',
    sasToken: 'your-sas-token'
  });

  const handleUpload = async () => {
    setUploading(true);
    setProgress(0);
    
    try {
      const result = await uploader.quickUploadFromLibrary(
        undefined, // Auto-generate filename
        { quality: 0.8, allowsEditing: true },
        (progressData) => {
          const percentage = Math.round(
            (progressData.totalBytesWritten / progressData.totalBytesExpectedToWrite) * 100
          );
          setProgress(percentage);
        }
      );
      
      if (result?.success) {
        setUploadedUrl(result.url);
      }
    } catch (error) {
      console.error('Upload failed:', error);
    } finally {
      setUploading(false);
    }
  };

  return (
    <View>
      <Button title="Upload Image" onPress={handleUpload} disabled={uploading} />
      
      {uploading && (
        <View>
          <Text>Uploading... {progress}%</Text>
          <ProgressBarAndroid styleAttr="Horizontal" progress={progress / 100} />
        </View>
      )}
      
      {uploadedUrl && (
        <Text>βœ… Upload successful! URL: {uploadedUrl}</Text>
      )}
    </View>
  );
};

πŸ›‘οΈ Security Best Practices

SAS Token Security

// βœ… Good: Fetch SAS tokens from your secure backend
const fetchSasToken = async () => {
  const response = await fetch('https://your-api.com/sas-token', {
    headers: { 'Authorization': `Bearer ${userToken}` }
  });
  return response.json();
};

// ❌ Bad: Never hardcode SAS tokens in your app
const badConfig = {
  storageAccount: 'account',
  containerName: 'container',
  sasToken: 'sv=2023-01-03&ss=b&srt=sco&sp=rwdlacx&se=2024-01-01T00:00:00Z&st=2023-01-01T00:00:00Z&spr=https&sig=...'
};

Token Permissions

Create SAS tokens with minimal required permissions:

# Minimum permissions for uploads
az storage container generate-sas \
  --account-name youraccount \
  --name yourcontainer \
  --permissions acw \
  --start 2024-01-01T00:00:00Z \
  --expiry 2024-01-02T00:00:00Z

πŸ” Error Handling

Common Error Scenarios

const handleUploadWithErrorHandling = async (fileUri: string) => {
  try {
    const result = await uploader.uploadFile(fileUri, 'image.jpg', 'image');
    
    if (!result.success) {
      switch (result.error) {
        case 'File does not exist':
          console.error('πŸ“ File not found');
          break;
        case 'File is empty':
          console.error('πŸ“„ Empty file');
          break;
        case 'File size exceeds maximum':
          console.error('πŸ“Š File too large');
          break;
        default:
          console.error('❌ Upload failed:', result.error);
      }
    }
    
    return result;
  } catch (error) {
    if (error.message.includes('Network')) {
      console.error('🌐 Network error - check internet connection');
    } else if (error.message.includes('Permission')) {
      console.error('πŸ” Permission denied - check SAS token');
    } else {
      console.error('πŸ’₯ Unexpected error:', error);
    }
    
    return { success: false, error: error.message };
  }
};

πŸ”§ Troubleshooting

Common Issues

1. "SAS token is invalid" Error

// Check token permissions and expiry
const config = uploader.getConfig();
console.log('Storage Account:', config.storageAccount);
console.log('Container:', config.containerName);
console.log('Base URL:', config.baseUrl);

2. "Permission denied" on iOS

// Check and request permissions
const permissions = await imageUploader.checkPermissions();
if (!permissions.mediaLibrary) {
  await imageUploader.requestMediaLibraryPermission();
}

3. "File not found" Error

// Verify file exists before upload
const fileInfo = await FileSystem.getInfoAsync(fileUri);
console.log('File exists:', fileInfo.exists);
console.log('File size:', fileInfo.size);

πŸ“ TypeScript Support

This library is written in TypeScript and includes complete type definitions:

import { 
  AzureBlobUploader, 
  ExpoImageUploader,
  AzureBlobConfig,
  UploadResult,
  UploadProgress,
  MediaType,
  ImagePickerOptions
} from 'expo-azure-blob-storage';

Type Definitions

interface UploadResult {
  success: boolean;
  fileName?: string;
  url?: string;
  size?: number;
  contentType?: string;
  error?: string;
}

interface UploadProgress {
  totalBytesWritten: number;
  totalBytesExpectedToWrite: number;
}

interface AzureBlobConfig {
  storageAccount: string;
  containerName: string;
  sasToken: string;
}

type MediaType = 'image' | 'video' | 'document';

🀝 Contributing

Contributions are welcome! Please read our contributing guidelines for details on our code of conduct and development process.

Development Setup

# Clone the repository
git clone https://github.com/katungi/expo-azure-blob-storage.git

# Install dependencies
npm install

# Run tests
npm test

# Build the library
npm run build

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ”— Links

❀️ Support

If you found this library helpful, please consider:

  • ⭐ Starring the repository
  • πŸ› Reporting bugs
  • πŸ’‘ Suggesting new features
  • πŸ“– Improving documentation

Made with ❀️ by Katungi Dennis

About

An NPM package that makes using azure-blob-service in react native easier (and type safe)

Resources

Stars

Watchers

Forks

Packages

No packages published