Charlie KrugThe Build Log

← All posts

A zip lists its own contents before you decompress a byte

Export X-Ray reads a data export's file index without decompressing it, so it can show what's in a 12,000-file Takeout zip within a second, entirely in your browser tab.

Ask Google for your data and you get a zip with anywhere from a few hundred to tens of thousands of files: nested JSON, HTML, CSV, media, all named after internal product codenames, no index, no summary. To find out what you actually got, you open files by hand, one at a time, hoping you stumble onto the interesting parts. The archive knows exactly what it contains. It just never tells you.

Except it kind of already has. A zip file keeps a table of every filename and size at the end of the archive, in a section called the central directory, and that table is stored uncompressed. You can read the whole file list, with byte sizes, without decompressing a single entry. That one fact is the trick Export X-Ray is built on.

Read the index first, decompress later

Drag a Takeout, Facebook, or Spotify export onto the page and it runs two passes. The first pass only reads that central directory: a decompression-free skim that gives it every filename and size in the archive. From filenames alone it can already tell you the total file count, which categories are present, and roughly how big each one is, within about a second of the drop, even for a 12,000-file zip. It also detects the provider here, purely from folder structure: Takeout's per-product folders, Facebook's your_activity_across_facebook layout, Spotify's MyData tree. No configuration, no "which service is this" dropdown. The archive's own shape is the answer.

Then the second pass does the slow work. It decompresses the files that actually hold records, counts them, and reads the earliest and latest timestamps, streaming each number onto the dashboard as it lands. Crucially this runs in yielding chunks rather than one blocking loop, so the tab stays responsive and a Cancel button stays live the entire time. You watch a scan beam sweep the panel and pop stat tiles into place: location points, photo count, oldest and newest capture date, streaming plays. The headline summary fills in live while the browser is still grinding through the zip. That live fill is the whole feel of the thing, and it exists only because the cheap index read can front- run the expensive decompression.

The zip never leaves the tab, and a test enforces it

Here is the design decision I'd keep even if you never open the app. Everything above happens in your browser. The file is read with JSZip and parsed on the page; there is no upload endpoint anywhere in the codebase, no telemetry, no third-party script that could see your files. This matters because of when people reach for a tool like this: usually right after they hit "download my data," which usually means they're deciding whether to delete the account. That is the worst possible moment to hand your entire personal archive to a stranger's server.

Saying "we don't upload" is cheap, so the repo doesn't just say it. There's a test, tests/privacy.test.ts, that fails the build if any future change ever calls fetch or XMLHttpRequest during a parse. The trust claim is wired to the CI gate: break the promise and the deploy stops. You can also just open the network tab while using it and watch zero requests carry your data. Both checks agree because there's nothing there to find.

What it doesn't do yet

Honest about the scope: v1 is breadth, not depth. It recognizes several categories across three providers well enough to give you a trustworthy top-line summary, but it does not crack open message content or every EXIF field. YouTube history might show as "thin" rather than fully parsed. An export format it doesn't recognize degrades to "unknown provider, N files found" plus a list of top-level folders, which is still more than the raw zip gave you, rather than a crash. Deeper per-category parsing and a timeline view are the next things on the list, once the summary layer is solid.

Try it

Drop a real Google Takeout, Facebook, or Spotify .zip on Export X-Ray and watch the first tiles appear before the progress bar has moved much: that's the index read finishing while decompression is still warming up. Let the rest stream in, then hit "Export summary" to save the whole breakdown as JSON for your records. If you'd rather read the parsers than trust me, they're all on GitHub, privacy test included.

Export X-Ray is live. Free, in your browser, no signup.

This post is part of the build log: every app my automated factory ships gets written up here, honestly. Browse everything at apps.charliekrug.com. Comments are open below.

Comments

Loading comments…