Fix Corrupt File Upload Errors — Why Uploaded Files Get Corrupted
Your uploaded files are corrupted — images won't open, PDFs show blank pages, ZIPs fail to extract. This is one of the most common and frustrating bugs in web development. Here's how to diagnose and fix it.
Common Error Messages
If you're googling one of these, you're in the right place:
"The file is damaged and could not be opened""This file does not appear to be a valid PDF""Not a JPEG file: starts with 0xef 0xbb""Error: Unexpected end of JSON input""Archive is corrupted or has an unexpected end""Error reading file: premature end of file"
Root Cause 1: Binary File Treated as Text
The #1 cause of file corruption during upload. If your server reads the file body as UTF-8 text instead of binary, every non-ASCII byte gets mangled.
Symptom: Uploaded file is slightly larger than original, images show garbage.
Fix (Node.js/Express):
// WRONG — body parsed as text
app.use(express.text({ limit: '10mb' }));
// RIGHT — use multer for file uploads
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('file'), (req, res) => {
// req.file.path contains the binary file
});
Root Cause 2: Wrong Content-Type Header
Sending a file with Content-Type: application/json instead of multipart/form-data corrupts the payload.
Symptom: Server receives garbled data, file size doesn't match.
Fix (Frontend):
// WRONG — manually setting Content-Type breaks multipart boundary
fetch('/api/upload', {
method: 'POST',
headers: { 'Content-Type': 'multipart/form-data' }, // DON'T DO THIS
body: formData,
});
// RIGHT — let the browser set Content-Type with boundary
fetch('/api/upload', {
method: 'POST',
body: formData, // Browser auto-sets Content-Type with boundary
});
Root Cause 3: Incomplete Transfer (Truncation)
Network timeout, client disconnect, or server body size limit causes partial upload.
Symptom: File is smaller than expected, opens partially or not at all.
Fix: Compare sizes and implement chunked upload:
app.post('/upload', upload.single('file'), (req, res) => {
const expectedSize = parseInt(req.headers['content-length']);
const actualSize = req.file.size;
if (actualSize !== expectedSize) {
fs.unlinkSync(req.file.path); // Delete corrupt file
return res.status(400).json({
error: 'Incomplete upload',
expected: expectedSize,
received: actualSize,
});
}
// File is complete
});
Root Cause 4: Base64 Encoding Issues
Sending binary files as base64 without proper decoding corrupts them.
// WRONG — double-encoding
const base64 = Buffer.from(file).toString('base64');
const decoded = Buffer.from(base64, 'utf8'); // WRONG encoding param
// RIGHT
const decoded = Buffer.from(base64, 'base64'); // Correct
Debug Checklist
- Compare file sizes:
ls -la original.pdf uploaded.pdf— should be identical - Compare checksums:
md5sum original.pdf uploaded.pdf - Check first bytes:
xxd original.pdf | head -1vsxxd uploaded.pdf | head -1 - Check Content-Type header: Must be
multipart/form-datawith boundary - Check server body parser: Must handle binary, not text
Test with Sample Files
Download files with known-good checksums to test your upload pipeline:
| Test | File | Expected | |------|------|----------| | Basic upload | sample-1mb.pdf | File opens correctly | | Image upload | sample-500kb.jpg | Image displays | | Large upload | sample-10mb.mp4 | Size matches exactly | | Already-corrupt | sample-corrupt.pdf | Your error handling works |
After uploading, verify: md5sum original.pdf === md5sum uploaded.pdf. If they differ, the upload pipeline is corrupting data.
Prevention
- Always use
multipart/form-datafor file uploads - Never manually set
Content-Typewhen usingFormData - Use multer (Node), Busboy, or formidable — not
express.json()for files - Validate file size after upload matches Content-Length
- Test with corrupted files to verify error handling