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

expo-azure-blob-storage

v0.0.2

Published

Easy Azure Blob Storage uploads for Expo and React Native applications

Readme

🚀 Expo Azure Blob Storage


✨ 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