File & PDF Tools

File Base64 Converter

Convert any file to or from Base64 and Data URLs.

File Base64 converter
Convert any file to or from Base64 text.
Local processing

File Base64 Conversion

Encode any file as raw Base64 or a MIME Data URL, or rebuild a file from Base64 input.

Usage

  • Input files for encoding are limited to 50 MB
  • Base64 is usually about one-third larger than the source file
  • Base64 is encoding, not encryption

Converts a local file into Base64 text or a complete data: URL, and rebuilds a downloadable file from Base64 pasted back in. It serves cases where binary bytes must travel through JSON fields, environment variables, or inline CSS, and where you need to see exactly what a data URI carries.

Why 50 MB becomes 69,905,068 characters

Output length follows 4 × ⌈n / 3⌉ characters for a file of n bytes, and no line breaks are ever inserted: RFC 4648 §3.1 forbids implementations from adding line feeds unless the citing specification demands it, whereas RFC 2045 MIME base64 wraps at 76 columns. The encode path stops at 50 MB, exactly 52,428,800 bytes, which expands to 69,905,068 characters; the decode box takes up to 70,000,000 characters, so the largest permitted file round-trips with 94,932 characters to spare and nothing larger does. Padding is proportionally brutal on tiny inputs — a 1-byte file yields 4 characters, a 300% increase — and the familiar one-third overhead of 33.33% is only the asymptote as n grows.

What the decode guard lets through before atob runs

Pasted text is trimmed, then stripped of every whitespace character, so a payload wrapped at 76 columns by a mail client still decodes. What remains must match ^[A-Za-z0-9+/]*={0,2}$ and must not have a length of 1 modulo 4. That guard is looser than the alphabet it implies: unpadded aGk passes and decodes, because the forgiving-base64 algorithm inside atob re-pads the short final group, yet the 11-character aGkg8J+Riw= also passes the guard and then throws inside atob, since that = sits in the first slot of a quartet it cannot belong to. Base64URL is rejected outright — - and _ fall outside the character class — so JWT segments and JWK members must be rewritten to + and / with their padding restored first.

Where the data: prefix actually comes from

Encoding runs FileReader.readAsDataURL, which serialises data:<type>;base64,<payload>. That type is the File object's own MIME string, assigned by the browser from the file extension at pick time; no magic bytes are sniffed, so a JPEG renamed to .png ships an image/png label over JPEG data. With the prefix checkbox off, the tool cuts at the first comma and returns the payload alone, which is safe because the base64 alphabet excludes commas. Decoding mirrors the logic: input matching data:(type)(;name=value)*;base64, donates its own type, an empty type falls back to the MIME field, and a percent-encoded data URI without ;base64, never matches, so its text is checked as a raw payload and rejected.

Worked example

Encoding a 7-byte text file that ends in an emoji

Input
note.txt holding the 7 bytes of hi 👋
Output
data:text/plain;base64,aGkg8J+Riw==

U+1F44B takes 4 bytes in UTF-8, so the file is 2 ASCII bytes, 1 space and 4 emoji bytes, 7 in total. That gives 4 × ⌈7 / 3⌉ = 12 characters, and the incomplete last group adds ==. Unchecking the prefix returns only aGkg8J+Riw==; pasting that back with the MIME field set to text/plain rebuilds the identical 7 bytes.

Limits and pitfalls

  • Files above 50 MB (52,428,800 bytes) are refused at selection and never encoded; there is no chunked or streaming mode.
  • The decode textarea is capped at 70,000,000 characters, measured after trimming — only 94,932 above what a maximum-size 50 MB file produces.
  • Input must use the standard alphabet: Base64URL containing - or _ fails validation instead of being converted, so translate it first.
  • The MIME field only labels the rebuilt file and never inspects the bytes, so a wrong value produces a download browsers handle as the wrong kind of file.
  • The output filename defaults to decoded-file.bin and cannot be recovered from the payload, because a data: URL carries no name; type it before downloading.

Related tools

When it goes wrong

The output is one unbroken line tens of thousands of characters long, and a mail gateway or a key parser rejects it.

CauseThe encode path dumps the FileReader.readAsDataURL result straight into the textarea and inserts no line breaks at all. RFC 4648 permits that, but PEM blocks and MIME bodies expect folding at 76 columns, and those validators count columns rather than characters.

FixFold the text to 76 characters per line before it goes into a .pem or a mail body (fold -w 76). Decoding here strips every whitespace character first, so a folded copy still round-trips. Keep it on one line for JSON values and environment variables, where an embedded newline truncates the value instead of being ignored.

Base64 copied from a JWT debugger is rejected as invalid, even though it decodes fine elsewhere.

CauseJWT segments and JWK members use the URL-safe alphabet of RFC 4648 §5, where - and _ stand in for + and /. The guard ^[A-Za-z0-9+/]*={0,2}$ runs before atob and has no room for those two characters.

FixMap - back to + and _ back to /. Padding does not have to be restored, because the forgiving decoder in atob re-pads the final group — but a payload whose length leaves 1 modulo 4 is refused regardless.

The decoded file will not open, or the desktop hands it to the wrong application.

CauseThe MIME field only becomes the type label on the rebuilt Blob; the bytes are never inspected. A pasted data: URL donates its own type, an empty type falls back to the field, and the field ships as application/octet-stream, which leaves the operating system guessing from the extension alone.

FixGive the output file its real extension and set the MIME field to match it, or paste the entire data: URL so the prefix supplies the type instead of you. The filename box is manual too — the default decoded-file.bin is never derived from the payload.

A tiny icon turns into text several times its own size.

CauseLength is 4 × ⌈n / 3⌉, so 1 byte becomes 4 characters, 2 bytes become 4, 3 bytes become 4 — a 300% jump on the smallest input. The one-third overhead only becomes the ceiling as n grows and padding stops mattering.

FixDo not route byte-scale assets through Base64: inline the SVG source directly or use a character reference. Encode only when the bytes have to cross a text-only channel such as a JSON field, an environment variable or a CSS declaration.

After choosing a file, the encode button stays greyed out and nothing happens.

CauseselectFile() writes an error the moment size exceeds 50 MB (52,428,800 bytes), and encode() bails out whenever an error message is set, so the button is disabled rather than failing on click.

FixCompress or split the file below 52,428,800 bytes and encode the pieces, or run base64 -w0 locally and paste the result into the decode side. The browser route holds the whole file for a single FileReader pass — there is no chunked mode to fall back on.

One concrete scenario

Five inline icons, one screenshot that stays a file

A theme package carries five SVG icons of 1,500 bytes each, and all five have to live inside one JSON config. Each encodes to 4 × ⌈1500 / 3⌉ = 2,000 characters — 1,500 is a multiple of 3, so no = padding is added and growth lands exactly on 33.33% — for 10,000 characters across the set. Tick the data: prefix so the extension supplies image/svg+xml, and the strings drop straight into the JSON. The same config also wanted a 20,000-byte screenshot: that is 4 × ⌈20000 / 3⌉ = 26,668 characters, more than double all five icons combined, in one unreadable line. Keep the five icons inline; ship the screenshot as a referenced file and spend the extra request.

Frequently Asked Questions

The file grew a lot after Base64 — is that normal?
Yes: Base64 encodes every 3 bytes as 4 characters, about 33% overhead. Inlining small images as Data URIs saves a request; large files do not benefit — they balloon and lose parallel download and independent caching.
Data URI versus plain Base64 — what is the difference?
Plain Base64 is just the data; a Data URI prefixes a type declaration: data:image/png;base64,xxxx. HTML and CSS require the prefixed form (the browser cannot guess the type); APIs usually take plain Base64 with the type agreed separately.
Is converting files here safe?
Entirely local — files never leave the browser, which is exactly why it suits contracts, scans, and ID photos. Remember Base64 is encoding, not encryption: anyone can decode the output, so add AES if confidentiality matters.

Content reviewed: