That happened to me recently while working on a project where I needed to send image files as Base64 strings. My code looked fine, but somehow the backend kept rejecting my upload.
After some debugging, I discovered the difference between two common approaches to Base64 conversion: using FileReader.readAsDataURL()
vs using arrayBuffer()
with btoa()
.
Here's what I learned 👇
The Common Way:
FileReader.readAsDataURL()
Most developers (including me) start with something like this:
const fileToBase64 = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const result = reader.result;
if (typeof result === 'string') {
const base64 = result.split(',')[1] || result;
resolve(base64);
} else {
resolve('');
}
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
};
This method reads the file and converts it into a data URL that looks like this:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA...
The first part (data:image/png;base64,
) describes the file type and encoding format, and the part after the comma is your actual Base64 data.
To extract just the Base64 portion, we typically split the string like this:
const base64 = result.split(',')[1];
⚠️ So Why Did It Fail?
It turns out this method isn't always reliable for API uploads.
- MIME prefix issues
Different browsers or file types might return different MIME(Multipurpose Internet Mail Extensions) headers (
data:application/octet-stream;base64,...
). If you don't handle that properly, yoursplit(',')[1]
might not behave as expected. - Encoding inconsistencies Some APIs reject the extra prefix or misinterpret the encoded content when it's not stripped correctly.
- Performance concerns
FileReader
loads the entire file into memory as a data URL string. For large files, this can cause noticeable slowdowns or even memory issues.
In short, FileReader
works great if you're displaying an image in the browser (like a preview), but not always when you're uploading it.
⚙️ The Reliable Way: arrayBuffer()
+ btoa()
Then I tried another method — and it worked perfectly.
const fileToBase64 = async (file) => {
const arrayBuffer = await file.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
let binary = '';
for (let i = 0; i < uint8Array.length; i++) {
binary += String.fromCharCode(uint8Array[i]);
}
return btoa(binary);
};
Why This Works Better
- It reads the raw binary data directly from the file.
- Converts it to a binary string manually.
- Uses
btoa()
to create a pure Base64 string (without thedata:
prefix). - Works consistently across browsers and file types.
When I used this version, my uploads started working instantly, no MIME(Multipurpose Internet Mail Extensions) confusion, no splitting, just clean Base64 data.
Quick Comparison

When to Use Which: Frontend preview →
FileReader
Backend upload →arrayBuffer
So the next time your Base64 upload mysteriously fails, remember it's not your API acting up, it's probably your conversion method. 🚀