I needed a 10 MB PDF to test an upload limit. Not "about 10 MB" — exactly 10,485,760 bytes. Every generator I found gave me fixed sizes or made me wait for a server download.
So I looked into doing it in the browser. The interesting part wasn't the size. It was keeping the file valid while hitting an arbitrary byte count.
The naive approach breaks the file
The obvious move is: make a real file, then append junk bytes until you hit the target.
const blob = new Blob([realPdf, padding]); // ← corrupt
For most formats this produces a broken file. A PDF reader follows the cross-reference table to a byte offset; trailing garbage after %%EOF can throw it off. A PNG decoder walks length-prefixed chunks; extra bytes at the end aren't a valid chunk.
The trick is that every format already reserves a legal place for extra data. You just have to put the padding there.
Padding where the spec allows it
-
PNG → a
tEXtmetadata chunk. It has a length prefix and a CRC-32, so decoders read exactly its declared length and skip the rest. Perfectly valid. - PDF → an unreferenced content stream. Same mechanism PDF writers use for incremental updates — the xref table just doesn't point at it.
- ZIP / DOCX / XLSX → a stored (uncompressed) entry inside the archive.
-
JPEG →
COMcomment segments, up to 65,533 bytes each.
For PNG, the padded chunk needs a correct CRC or the decoder rejects it:
function pngChunk(type, data) {
const body = concat(type, data);
const crc = crc32(body); // must be correct
return concat(uint32(data.length), body, uint32(crc));
}
Get the CRC right and the file opens everywhere — the padding is invisible.
Why the browser part matters
Because nothing crosses the network, size barely affects speed. Assembling a 100 MB file is a memory operation, not a download — it lands in tens of milliseconds. And the file never leaves your machine, so there's no upload, no storage, nothing to log.
I verified the output against real parsers — pypdf reads the PDF text, Pillow decodes the PNG, openpyxl opens the XLSX. They're genuinely valid files, just padded.
I turned this into a free tool (ByteRivet) that does 19 formats at any exact size, all in-browser. But the padding-without-corruption idea is the reusable part — worth knowing whenever you need a fixture at a precise size.
What's the weirdest exact-size requirement you've hit in testing?
Top comments (1)
One format fought back harder than the rest: PDF.
The padding goes into a stream object, but a PDF's cross-reference table records the exact byte offset of every object. The moment you grow the padding stream, every offset after it shifts — including the startxref value at the very end that points back to the table. So the byte count you write changes the byte count you need.
I ended up solving the length analytically instead of guessing: compute where everything lands, write the padding in one pass, done. Trying to converge by re-generating the file in a loop was the naive version — and on a 100 MB file that loop cost seconds. The single-pass version does it in ~40ms.
WAV was the opposite kind of lesson — a naive sin() per sample was fine at 1 MB and unbearable at 100 MB. Tiling one pre-computed wave period took it from 10 seconds to under 100ms.