Download files from URLs with progress tracking, resume capabilities, custom headers, and destination control for efficient network file operations.
Download files from remote URLs with comprehensive options and error handling.
/**
* Downloads a file from a URL to a specified destination
* @param url URL of the file to download
* @param destination Target directory or file path
* @param options Download configuration options
* @returns Promise resolving to the downloaded File instance
*/
static downloadFileAsync(
url: string,
destination: Directory | File,
options?: DownloadOptions
): Promise<File>;
interface DownloadOptions {
/** HTTP headers to send with the download request */
headers?: { [key: string]: string };
}Usage Examples:
import { File, Directory, Paths } from "expo-file-system";
// Basic file download to directory
const downloadDir = new Directory(Paths.document, "downloads");
downloadDir.create();
const imageUrl = "https://example.com/images/photo.jpg";
const downloadedImage = await File.downloadFileAsync(imageUrl, downloadDir);
console.log(`Downloaded: ${downloadedImage.name}`);
console.log(`Size: ${downloadedImage.size} bytes`);
console.log(`Location: ${downloadedImage.uri}`);
// Download to specific file path
const specificFile = new File(Paths.document, "profile-picture.jpg");
const profileImage = await File.downloadFileAsync(imageUrl, specificFile);
// Download with custom headers
const authenticatedDownload = await File.downloadFileAsync(
"https://api.example.com/secure/document.pdf",
downloadDir,
{
headers: {
"Authorization": "Bearer your-auth-token",
"User-Agent": "MyApp/1.0.0",
"Accept": "application/pdf"
}
}
);Monitor download progress and handle large file downloads efficiently.
Usage Examples:
import { File, Directory, Paths } from "expo-file-system";
// Download with progress monitoring
async function downloadWithProgress(
url: string,
destination: Directory,
onProgress?: (bytesDownloaded: number, totalBytes: number) => void
) {
console.log(`Starting download: ${url}`);
try {
// Start download
const downloadedFile = await File.downloadFileAsync(url, destination);
// Note: Progress tracking would typically be implemented with
// custom networking libraries or by checking file size periodically
console.log(`Download completed: ${downloadedFile.name}`);
console.log(`Final size: ${downloadedFile.size} bytes`);
return downloadedFile;
} catch (error) {
console.error("Download failed:", error);
throw error;
}
}
// Download queue management
class DownloadManager {
private downloadQueue: Array<{
url: string;
destination: Directory | File;
options?: DownloadOptions;
}> = [];
private activeDownloads = 0;
private maxConcurrentDownloads = 3;
async addDownload(
url: string,
destination: Directory | File,
options?: DownloadOptions
) {
this.downloadQueue.push({ url, destination, options });
this.processQueue();
}
private async processQueue() {
if (this.activeDownloads >= this.maxConcurrentDownloads || this.downloadQueue.length === 0) {
return;
}
const download = this.downloadQueue.shift()!;
this.activeDownloads++;
try {
console.log(`Starting download: ${download.url}`);
const file = await File.downloadFileAsync(
download.url,
download.destination,
download.options
);
console.log(`Completed download: ${file.name}`);
} catch (error) {
console.error(`Download failed: ${download.url}`, error);
} finally {
this.activeDownloads--;
// Process next item in queue
setTimeout(() => this.processQueue(), 100);
}
}
}
// Usage
const downloadManager = new DownloadManager();
const downloadDir = new Directory(Paths.document, "downloads");
await downloadManager.addDownload("https://example.com/file1.zip", downloadDir);
await downloadManager.addDownload("https://example.com/file2.pdf", downloadDir);
await downloadManager.addDownload("https://example.com/file3.jpg", downloadDir);Robust error handling for network operations with retry mechanisms.
Usage Examples:
import { File, Directory, Paths } from "expo-file-system";
// Download with retry logic
async function downloadWithRetry(
url: string,
destination: Directory | File,
options?: DownloadOptions,
maxRetries: number = 3
): Promise<File> {
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
console.log(`Download attempt ${attempt}/${maxRetries}: ${url}`);
const file = await File.downloadFileAsync(url, destination, options);
console.log(`Download successful on attempt ${attempt}`);
return file;
} catch (error) {
lastError = error as Error;
console.warn(`Download attempt ${attempt} failed:`, error);
if (attempt < maxRetries) {
// Exponential backoff
const delay = Math.pow(2, attempt) * 1000;
console.log(`Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw new Error(`Download failed after ${maxRetries} attempts: ${lastError.message}`);
}
// Validate downloaded files
async function downloadAndValidate(
url: string,
destination: Directory,
expectedSize?: number,
expectedChecksum?: string
): Promise<File> {
const downloadedFile = await downloadWithRetry(url, destination);
// Validate file size
if (expectedSize && downloadedFile.size !== expectedSize) {
downloadedFile.delete();
throw new Error(`File size mismatch: expected ${expectedSize}, got ${downloadedFile.size}`);
}
// Validate checksum
if (expectedChecksum) {
const fileInfo = downloadedFile.info({ md5: true });
if (fileInfo.md5 !== expectedChecksum) {
downloadedFile.delete();
throw new Error(`Checksum mismatch: expected ${expectedChecksum}, got ${fileInfo.md5}`);
}
}
console.log("File validation successful");
return downloadedFile;
}
// Usage with validation
try {
const validatedFile = await downloadAndValidate(
"https://example.com/important-document.pdf",
new Directory(Paths.document, "verified"),
1024 * 1024, // Expected 1MB
"5d41402abc4b2a76b9719d911017c592" // Expected MD5
);
console.log(`Verified download: ${validatedFile.name}`);
} catch (error) {
console.error("Download validation failed:", error);
}Efficient batch downloading with progress tracking and error recovery.
Usage Examples:
import { File, Directory, Paths } from "expo-file-system";
interface DownloadTask {
url: string;
filename?: string;
headers?: { [key: string]: string };
}
// Batch download with comprehensive tracking
async function batchDownload(
tasks: DownloadTask[],
destination: Directory,
onProgress?: (completed: number, total: number, currentFile?: string) => void
): Promise<File[]> {
const results: File[] = [];
const errors: Array<{ task: DownloadTask; error: Error }> = [];
for (let i = 0; i < tasks.length; i++) {
const task = tasks[i];
try {
onProgress?.(i, tasks.length, task.filename || task.url);
const targetFile = task.filename
? new File(destination, task.filename)
: destination;
const downloadedFile = await File.downloadFileAsync(
task.url,
targetFile,
{ headers: task.headers }
);
results.push(downloadedFile);
console.log(`Downloaded ${i + 1}/${tasks.length}: ${downloadedFile.name}`);
} catch (error) {
console.error(`Failed to download ${task.url}:`, error);
errors.push({ task, error: error as Error });
}
}
onProgress?.(tasks.length, tasks.length);
if (errors.length > 0) {
console.warn(`Batch download completed with ${errors.length} errors`);
// Optionally retry failed downloads or report errors
}
return results;
}
// Download manifest file and process contents
async function downloadFromManifest(manifestUrl: string): Promise<File[]> {
const manifestFile = await File.downloadFileAsync(
manifestUrl,
new File(Paths.cache, "download-manifest.json")
);
const manifestContent = await manifestFile.text();
const manifest = JSON.parse(manifestContent);
const downloadTasks: DownloadTask[] = manifest.files.map((item: any) => ({
url: item.url,
filename: item.name,
headers: item.headers
}));
const downloadDir = new Directory(Paths.document, "manifest-downloads");
downloadDir.create();
const downloadedFiles = await batchDownload(
downloadTasks,
downloadDir,
(completed, total, currentFile) => {
console.log(`Progress: ${completed}/${total} - ${currentFile}`);
}
);
// Clean up manifest file
manifestFile.delete();
return downloadedFiles;
}
// Smart caching with download fallback
async function getFileWithCache(
url: string,
cacheKey: string,
maxAge: number = 24 * 60 * 60 * 1000 // 24 hours
): Promise<File> {
const cacheDir = new Directory(Paths.cache, "downloaded-files");
cacheDir.create();
const cachedFile = new File(cacheDir, `${cacheKey}.cached`);
// Check if cached file exists and is fresh
if (cachedFile.exists) {
const fileInfo = cachedFile.info();
const age = Date.now() - (fileInfo.modificationTime || 0);
if (age < maxAge) {
console.log(`Using cached file: ${cacheKey}`);
return cachedFile;
} else {
console.log(`Cache expired for: ${cacheKey}`);
cachedFile.delete();
}
}
// Download fresh copy
console.log(`Downloading fresh copy: ${url}`);
const downloadedFile = await File.downloadFileAsync(url, cachedFile);
return downloadedFile;
}
// Usage examples
async function demonstrateNetworkOperations() {
// Batch download example
const downloadTasks: DownloadTask[] = [
{
url: "https://example.com/image1.jpg",
filename: "image1.jpg"
},
{
url: "https://api.example.com/data.json",
filename: "api-data.json",
headers: { "Authorization": "Bearer token123" }
},
{
url: "https://example.com/document.pdf",
filename: "document.pdf"
}
];
const downloadDir = new Directory(Paths.document, "batch-downloads");
downloadDir.create();
const downloadedFiles = await batchDownload(downloadTasks, downloadDir);
console.log(`Successfully downloaded ${downloadedFiles.length} files`);
// Smart caching example
const apiData = await getFileWithCache(
"https://api.example.com/config.json",
"app-config",
30 * 60 * 1000 // 30 minutes cache
);
const config = JSON.parse(await apiData.text());
console.log("Loaded configuration:", config);
}Handle authenticated downloads and custom HTTP headers for API access.
Usage Examples:
import { File, Directory, Paths } from "expo-file-system";
// Authenticated download service
class AuthenticatedDownloadService {
private authToken: string | null = null;
private refreshTokenUrl: string;
constructor(refreshTokenUrl: string) {
this.refreshTokenUrl = refreshTokenUrl;
}
async setAuthToken(token: string) {
this.authToken = token;
}
async refreshAuthToken(): Promise<string> {
// Implementation would depend on your auth system
const response = await fetch(this.refreshTokenUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh_token: this.authToken })
});
const result = await response.json();
this.authToken = result.access_token;
return this.authToken;
}
async downloadAuthenticatedFile(
url: string,
destination: Directory | File,
additionalHeaders?: Record<string, string>
): Promise<File> {
let attempts = 0;
const maxAttempts = 2;
while (attempts < maxAttempts) {
try {
const headers = {
'Authorization': `Bearer ${this.authToken}`,
'User-Agent': 'MyApp/1.0.0',
...additionalHeaders
};
return await File.downloadFileAsync(url, destination, { headers });
} catch (error) {
attempts++;
// If unauthorized and we haven't tried refreshing, refresh token
if (error && attempts === 1) {
console.log("Auth failed, refreshing token...");
await this.refreshAuthToken();
} else {
throw error;
}
}
}
throw new Error("Download failed after token refresh");
}
}
// API-specific download patterns
class APIDownloadClient {
private baseUrl: string;
private apiKey: string;
constructor(baseUrl: string, apiKey: string) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
async downloadReport(
reportId: string,
format: 'pdf' | 'xlsx' | 'csv',
destination: Directory
): Promise<File> {
const url = `${this.baseUrl}/reports/${reportId}/export?format=${format}`;
const filename = `report-${reportId}.${format}`;
const headers = {
'X-API-Key': this.apiKey,
'Accept': this.getContentType(format),
'X-Request-ID': this.generateRequestId()
};
const targetFile = new File(destination, filename);
return await File.downloadFileAsync(url, targetFile, { headers });
}
async downloadWithRateLimit(
url: string,
destination: Directory | File,
rateLimitDelay: number = 1000
): Promise<File> {
// Implement rate limiting
await new Promise(resolve => setTimeout(resolve, rateLimitDelay));
const headers = {
'X-API-Key': this.apiKey,
'X-Rate-Limit': 'respect'
};
return await File.downloadFileAsync(url, destination, { headers });
}
private getContentType(format: string): string {
const types = {
'pdf': 'application/pdf',
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'csv': 'text/csv'
};
return types[format as keyof typeof types] || 'application/octet-stream';
}
private generateRequestId(): string {
return Math.random().toString(36).substring(2, 15);
}
}
// Usage examples
async function demonstrateAuthenticatedDownloads() {
// Authenticated service usage
const authService = new AuthenticatedDownloadService("https://api.example.com/auth/refresh");
await authService.setAuthToken("initial-token");
const protectedFile = await authService.downloadAuthenticatedFile(
"https://api.example.com/protected/document.pdf",
new Directory(Paths.document, "secure")
);
// API client usage
const apiClient = new APIDownloadClient("https://api.example.com", "your-api-key");
const reportsDir = new Directory(Paths.document, "reports");
reportsDir.create();
const pdfReport = await apiClient.downloadReport("report-123", "pdf", reportsDir);
const excelReport = await apiClient.downloadReport("report-456", "xlsx", reportsDir);
console.log(`Downloaded reports: ${pdfReport.name}, ${excelReport.name}`);
}