How to Preview S3 Objects Without Downloading the Whole File
An object listing can tell you a key, a size and a modification date. It cannot tell you whether a 40 GB video is the right cut, what columns are in a Parquet file or which files an archive contains.
Downloading the whole object answers those questions, but it is a poor default. It adds waiting, egress and a temporary file that has to be cleaned up. Most file formats already contain a smaller structure that answers the first useful questions. The trick is to request that structure instead of the payload.
Cargocat does this with three layers: a small content probe, a range-capable server on the local machine and viewers that ask for only the bytes their format needs.
Start with a small content probe
The first request reads a few kilobytes from the beginning of the object.
Cargocat checks those bytes for recognizable signatures, then uses the object
name and its declared Content-Type as supporting hints.
That order matters. Object stores commonly contain files uploaded as
application/octet-stream, files with no extension and files whose metadata is
simply wrong. A PNG called data.bin should still open as an image; a ZIP
labeled image/jpeg should not be sent to an image decoder.
Once the format is known, the selected viewer receives the object’s size, detected media type and a way to read byte ranges. It does not receive storage credentials or a provider client.
Put one local URL in front of every provider
Browser-based viewers expect ordinary web primitives: an image src, a video
URL, PDF range requests or fetch(). Giving them a bucket URL directly creates
two problems. The bucket needs a compatible CORS policy, and a signed URL is a
bearer credential that works outside the app until it expires.
Cargocat instead runs a small HTTP server bound to loopback. A viewer talks to a
127.0.0.1 URL, and the native storage layer makes the authenticated request to
the provider. When the viewer sends an HTTP Range header, the local server
forwards that bounded read.
The result is one transport for Amazon S3, S3-compatible providers and local folders. The browser never addresses the bucket, so previewing does not require changing the bucket’s CORS policy. View code also never receives a presigned provider URL.
Let the file format choose the ranges
Partial preview works because many formats keep their table of contents or metadata in a known place.
| Format or view | Bytes requested | What that reveals |
|---|---|---|
| ZIP | Up to the last 66 KB | The central directory: names, sizes, compression methods and timestamps, when the directory fits in that tail window |
| Parquet | The final 8 bytes, then the exact footer range | Schema, row count, row groups, codecs and column statistics |
| MP4 | The container index and the ranges it points to | Tracks, codecs, dimensions, frame rate and other media details, usually from a few hundred kilobytes |
| Video and audio | The ranges requested by the media player | Playback and seeking without transferring the parts that are never watched |
| Text, logs and configs | A prefix of at most 8 MiB | Useful content immediately, with a notice when the view is truncated |
Parquet is a particularly clear example. Its last eight bytes contain a footer
length and the PAR1 marker. Cargocat reads those eight bytes, calculates where
the metadata begins, then asks for precisely that range. A multi-gigabyte table
can expose its schema and row count without reading its column data.
ZIP uses the same idea in reverse. The End of Central Directory record sits at the tail and points back to the archive’s index. Reading the tail can therefore produce a useful listing without reading any compressed file contents.
For MP4 containers, the parser follows the moov index through range requests.
Media playback uses the same local URL, so the operating system’s player can
seek normally and fetch the sections it needs.
Partial does not mean every format is random-access
Range requests cannot invent an index that a format does not have. TAR, for example, stores headers sequentially and has no central directory. Listing a TAR means reading through it, subject to the viewer’s size limits. A ZIP whose central directory is larger than the tail window also needs more of the file before it can be listed.
Small objects may be read in full because they already fit under a viewer’s limit. The useful guarantee is not that every preview uses the smallest possible byte count. It is that each viewer makes bounded, format-aware reads instead of treating a full download as the universal first step.
Compressed objects need a different bound
Gzip and zstd streams are sequential: the decoded byte at an arbitrary offset cannot usually be fetched on its own. For those objects, Cargocat reads compressed chunks until it has decoded at most an 8 MiB prefix, then detects the format again from the decoded bytes.
That is why events.json.gz, server.log.gz and table.csv.zst can open in
their normal JSON, log and table viewers. The viewer is told when it has only a
prefix, so a large compressed object cannot quietly turn into an unbounded
transfer.
Keep third-party renderers behind a narrower boundary
Built-in parsers receive a read-only viewer context: name, path, size, media type, loopback URL and range readers. They get no connection ID, provider credential or general native command channel.
Renderers built on third-party engines are isolated further. They run in a sandboxed frame with no network access and receive only the bytes the host sends them. They never receive the loopback URL, because its token can read an object and should not cross into code outside the app’s control.
The same range architecture therefore does two jobs. It avoids unnecessary transfer, and it creates a small interface between storage access and the code that interprets a file.
Preview one in place
You can try the built-in previews in the browser, read the complete supported-format guide, or download Cargocat for macOS, Windows or Linux.