Last active
February 17, 2025 10:02
-
-
Save hexadeciman/1ef3007c65c1bb2667d9616705947ae3 to your computer and use it in GitHub Desktop.
A robust utility function to download data as a JSON file, supporting large datasets, circular reference handling, optional timestamp inclusion, and automatic splitting into smaller chunks for oversized data.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Configuration options for JSON download | |
*/ | |
interface DownloadJsonOptions { | |
filename?: string; | |
indent?: number; | |
includeTimestamp?: boolean; | |
prettify?: boolean; | |
chunkSize?: number; // Size in MB for splitting large data | |
} | |
/** | |
* Enhanced utility function to download data as a JSON file | |
* Handles large datasets and circular references | |
* @param data - The data to be downloaded | |
* @param options - Configuration options | |
*/ | |
export function downloadAsJson(data: unknown, options: DownloadJsonOptions = {}): void { | |
const { | |
filename = "data", | |
indent = 2, | |
includeTimestamp = false, | |
prettify = true, | |
chunkSize = 700, // Default 50MB chunks | |
} = options; | |
try { | |
// Prepare the data | |
let finalData = data; | |
if (includeTimestamp) { | |
finalData = { | |
data, | |
timestamp: new Date().toISOString(), | |
metadata: { | |
exportedAt: new Date().toISOString(), | |
browser: navigator.userAgent, | |
}, | |
}; | |
} | |
// Handle circular references | |
const seen = new WeakSet(); | |
const sanitizedData = JSON.stringify( | |
finalData, | |
(key, value) => { | |
if (typeof value === "object" && value !== null) { | |
if (seen.has(value)) { | |
return "[Circular Reference]"; | |
} | |
seen.add(value); | |
} | |
return value; | |
}, | |
prettify ? indent : undefined, | |
); | |
// Check size of the string | |
const bytes = new TextEncoder().encode(sanitizedData).length; | |
const maxBytes = chunkSize * 1024 * 1024; // Convert MB to bytes | |
if (bytes > maxBytes) { | |
// For large datasets, split into multiple files | |
handleLargeDownload(finalData, options); | |
return; | |
} | |
// Create blob and trigger download | |
const blob = new Blob([sanitizedData], { | |
type: "application/json", | |
}); | |
downloadBlob(blob, filename); | |
} catch (error) { | |
// console.error("Error downloading JSON file:", error); | |
throw new Error(`Failed to download JSON file: ${error instanceof Error ? error.message : "Unknown error"}`); | |
} | |
} | |
/** | |
* Helper function to handle large datasets by splitting them into chunks | |
*/ | |
function handleLargeDownload(data: unknown, options: DownloadJsonOptions): void { | |
if (Array.isArray(data)) { | |
// If data is an array, split it into chunks | |
const chunkSize = 1000; // Number of items per chunk | |
for (let i = 0; i < data.length; i += chunkSize) { | |
const chunk = data.slice(i, i + chunkSize); | |
const chunkFilename = `${options.filename || "data"}_part${Math.floor(i / chunkSize) + 1}`; | |
const sanitizedChunk = JSON.stringify(chunk, null, options.prettify ? options.indent || 2 : undefined); | |
const blob = new Blob([sanitizedChunk], { type: "application/json" }); | |
downloadBlob(blob, chunkFilename); | |
} | |
} else if (typeof data === "object" && data !== null) { | |
// If data is an object, split by keys | |
const keys = Object.keys(data); | |
const chunkSize = Math.ceil(keys.length / 4); // Split into 4 parts | |
for (let i = 0; i < keys.length; i += chunkSize) { | |
const chunkKeys = keys.slice(i, i + chunkSize); | |
const chunk = chunkKeys.reduce( | |
(acc, key) => { | |
acc[key] = data[key as keyof typeof data]; | |
return acc; | |
}, | |
{} as Record<string, unknown>, | |
); | |
const chunkFilename = `${options.filename || "data"}_part${Math.floor(i / chunkSize) + 1}`; | |
const sanitizedChunk = JSON.stringify(chunk, null, options.prettify ? options.indent || 2 : undefined); | |
const blob = new Blob([sanitizedChunk], { type: "application/json" }); | |
downloadBlob(blob, chunkFilename); | |
} | |
} | |
} | |
/** | |
* Helper function to handle the actual download | |
*/ | |
function downloadBlob(blob: Blob, filename: string): void { | |
const fullFilename = filename.endsWith(".json") ? filename : `${filename}.json`; | |
const url = URL.createObjectURL(blob); | |
const link = document.createElement("a"); | |
link.href = url; | |
link.download = fullFilename; | |
link.style.display = "none"; | |
document.body.appendChild(link); | |
link.click(); | |
document.body.removeChild(link); | |
setTimeout(() => { | |
URL.revokeObjectURL(url); | |
}, 100); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment