Robust file operations including remote file downloading with built-in caching, retry logic, authentication, and comprehensive error handling. These utilities are essential for Gatsby's asset processing and build pipeline.
Downloads remote files with advanced features including caching, retry logic, authentication, and progress tracking.
/**
* Downloads a remote file to disk with caching and retry logic
* @param args - Configuration object for the download operation
* @returns Promise resolving to the local file path of the downloaded file
* @throws Error if download fails after all retries
*/
function fetchRemoteFile(args: IFetchRemoteFileOptions): Promise<string>;
interface IFetchRemoteFileOptions {
/** URL to download from */
url: string;
/** HTTP authentication credentials */
auth?: {
htaccess_pass?: string;
htaccess_user?: string;
};
/** Additional HTTP headers to send with the request */
httpHeaders?: Headers;
/** File extension override (auto-detected if not provided) */
ext?: string;
/** Filename override (extracted from URL if not provided) */
name?: string;
/** Cache key for persistent caching across builds */
cacheKey?: string;
/** Whether to exclude content digest from the file path */
excludeDigest?: boolean;
/** Local directory to save the file (required if cache not provided) */
directory?: string;
/** Gatsby cache instance for persistent caching (required if directory not provided) */
cache?: GatsbyCache;
}Usage Examples:
import { fetchRemoteFile } from "gatsby-core-utils";
// Basic file download
const imagePath = await fetchRemoteFile({
url: "https://example.com/hero-image.jpg",
directory: "./public/images"
});
console.log(`Downloaded to: ${imagePath}`);
// Download with authentication
const protectedAsset = await fetchRemoteFile({
url: "https://api.example.com/private/document.pdf",
directory: "./downloads",
auth: {
htaccess_user: "admin",
htaccess_pass: "secret123"
}
});
// Download with custom headers and caching
const apiResponse = await fetchRemoteFile({
url: "https://api.example.com/data.json",
directory: "./cache",
httpHeaders: new Headers({
"User-Agent": "GatsbyBot/1.0",
"Accept": "application/json"
}),
cacheKey: "api-data-v1",
name: "latest-data",
ext: ".json"
});
// Using Gatsby cache for persistent storage
const cachedImage = await fetchRemoteFile({
url: "https://cdn.example.com/banner.png",
cache: gatsbyCache,
cacheKey: `banner-${contentHash}`
});Creates sanitized file paths with optional content-based hashing for cache busting.
/**
* Creates a file path with sanitized filename and optional hash
* @param directory - Target directory path
* @param filename - Base filename (will be sanitized)
* @param ext - File extension including the dot
* @returns Complete file path with sanitized filename
*/
function createFilePath(directory: string, filename: string, ext: string): string;Usage Examples:
import { createFilePath } from "gatsby-core-utils";
// Create safe file paths
const imagePath = createFilePath("./public/images", "My Photo!", ".jpg");
console.log(imagePath); // "./public/images/My-Photo!.jpg" (sanitized)
// Handle problematic characters
const docPath = createFilePath("/docs", "file<>with|bad*chars", ".pdf");
console.log(docPath); // "/docs/file-with-bad-chars.pdf"
// Long filenames get hashed
const longName = "a".repeat(300);
const hashedPath = createFilePath("./cache", longName, ".txt");
console.log(hashedPath); // "./cache/aaaaa...-a1b2c3d4.txt" (truncated + hash)Extracts filenames and extensions from URLs for automatic naming.
/**
* Extracts filename from URL
* @param url - URL to parse
* @returns Decoded filename from the URL path
*/
function getRemoteFileName(url: string): string;
/**
* Extracts file extension from URL
* @param url - URL to parse
* @returns File extension including the dot (e.g., ".jpg")
*/
function getRemoteFileExtension(url: string): string;Usage Examples:
import { getRemoteFileName, getRemoteFileExtension } from "gatsby-core-utils";
// Extract filename from URL
const url = "https://example.com/assets/hero%20image.jpg?v=123";
const filename = getRemoteFileName(url);
console.log(filename); // "hero image.jpg" (decoded)
const extension = getRemoteFileExtension(url);
console.log(extension); // ".jpg"
// Use for automatic file naming
const downloadPath = await fetchRemoteFile({
url: "https://api.example.com/files/document.pdf",
directory: "./downloads",
name: getRemoteFileName(url).replace(/\.[^/.]+$/, ""), // Remove extension
ext: getRemoteFileExtension(url)
});Creates short hashes for filename uniqueness and cache busting.
/**
* Creates a SHA1 hash of input, truncated to specified length
* @param input - String to hash
* @param length - Length of hash to return (default: 8)
* @returns Truncated hexadecimal hash string
*/
function createFileHash(input: string, length?: number): string;Usage Examples:
import { createFileHash } from "gatsby-core-utils";
// Create short hashes for cache busting
const contentHash = createFileHash("file content here");
console.log(contentHash); // "a1b2c3d4" (8 characters by default)
// Custom hash length
const longHash = createFileHash("file content", 16);
console.log(longHash); // "a1b2c3d4e5f6g7h8" (16 characters)
// Use in filename generation
const hashedFilename = `bundle-${createFileHash(bundleContent)}.js`;
console.log(hashedFilename); // "bundle-a1b2c3d4.js"Memory-efficient MD5 hashing for large files using streams.
/**
* Creates MD5 hash from a file using streams for memory efficiency
* @param filePath - Absolute path to the file to hash
* @returns Promise resolving to MD5 hash in hexadecimal format
*/
function md5File(filePath: string): Promise<string>;Usage Examples:
import { md5File } from "gatsby-core-utils";
// Hash files for content verification
const downloadedFile = await fetchRemoteFile({
url: "https://example.com/large-file.zip",
directory: "./downloads"
});
const fileHash = await md5File(downloadedFile);
console.log(`File hash: ${fileHash}`);
// Verify file integrity
const expectedHash = "d41d8cd98f00b204e9800998ecf8427e";
if (fileHash === expectedHash) {
console.log("File integrity verified");
} else {
console.log("File may be corrupted");
}Remote file downloads include sophisticated retry logic with configurable timeouts:
// Environment variables for retry configuration
process.env.GATSBY_CONCURRENT_DOWNLOAD = "50"; // Max concurrent downloads
process.env.GATSBY_STALL_RETRY_LIMIT = "3"; // Retry attempts for stalled downloads
process.env.GATSBY_STALL_TIMEOUT = "30000"; // Stall timeout in milliseconds
process.env.GATSBY_CONNECTION_TIMEOUT = "30000"; // Connection timeout
process.env.GATSBY_INCOMPLETE_RETRY_LIMIT = "3"; // Retry attempts for incomplete downloadsimport { fetchRemoteFile } from "gatsby-core-utils";
// Persistent caching with Gatsby cache
const persistentFile = await fetchRemoteFile({
url: "https://api.example.com/data.json",
cache: gatsbyCache,
cacheKey: `api-data-${version}`, // Version-based cache invalidation
});
// Directory-based caching
const localFile = await fetchRemoteFile({
url: "https://example.com/image.jpg",
directory: "./public/images",
excludeDigest: false, // Include content hash in filename for cache busting
});import { fetchRemoteFile } from "gatsby-core-utils";
// HTTP Basic Authentication
const authenticatedFile = await fetchRemoteFile({
url: "https://protected.example.com/file.pdf",
directory: "./downloads",
auth: {
htaccess_user: process.env.API_USERNAME,
htaccess_pass: process.env.API_PASSWORD
}
});
// Custom headers for API authentication
const apiFile = await fetchRemoteFile({
url: "https://api.example.com/secure/data.json",
directory: "./cache",
httpHeaders: new Headers({
"Authorization": `Bearer ${process.env.API_TOKEN}`,
"X-API-Key": process.env.API_KEY
})
});File operation functions include comprehensive error handling:
import { fetchRemoteFile } from "gatsby-core-utils";
try {
const filePath = await fetchRemoteFile({
url: "https://example.com/file.jpg",
directory: "./downloads"
});
console.log(`Success: ${filePath}`);
} catch (error) {
if (error.code === 'EAUTH') {
console.error('Authentication failed');
} else if (error.code === 'ENOTFOUND') {
console.error('Network error - host not found');
} else if (error.code === 'ENOSPC') {
console.error('Insufficient disk space');
} else {
console.error(`Download failed: ${error.message}`);
}
}These file operations are extensively used throughout Gatsby: