jQuery AJAX File Upload with FormData to PHP Guide
Learn how to upload files using jQuery.ajax() and FormData with multipart/form-data to PHP. Fix empty $_POST issues, add progress bars, and ensure Safari 5+ compatibility for seamless file uploads.
How to upload a file using jQuery.ajax() with multipart/form-data to a PHP server-side script?
I need to send a file selected from an HTML file input to a PHP script using jQuery’s $.ajax() method. I can retrieve the FileList with $('#fileinput').attr('files'), but the $_POST array on the server side is empty (NULL).
Current code:
$.ajax({
url: 'php/upload.php',
data: $('#file').attr('files'),
cache: false,
contentType: 'multipart/form-data',
processData: false,
type: 'POST',
success: function(data){
alert(data);
}
});
This must work in Safari 5 (FF and Chrome preferred). Note that file uploads via XHR/AJAX are possible.
To upload a file using jQuery.ajax() with multipart/form-data to a PHP script, wrap your file input’s FileList in a FormData object and send it with contentType: false and processData: false—this prevents jQuery from mangling the binary data and ensures PHP receives it in $_FILES. Your current code fails because $('#file').attr('files') returns a string, not the actual FileList, leaving $_POST empty; grab files via $('#file')[0].files[0] instead. This approach works reliably in Safari 5+, Firefox, and Chrome, fixing common jQuery AJAX file upload pitfalls.
Contents
- Understanding File Uploads with jQuery AJAX and multipart/form-data
- Why Your Current Code Fails
- Creating FormData for jQuery AJAX File Uploads
- Complete jQuery AJAX Code Example
- PHP Server-Side Handling for Uploads
- Browser Compatibility for Safari 5 and Beyond
- Advanced Tips: Multiple Files and Troubleshooting
- Sources
- Conclusion
Understanding File Uploads with jQuery AJAX and multipart/form-data
File uploads via jQuery AJAX aren’t as straightforward as sending JSON or form data. Traditional forms use enctype="multipart/form-data", which creates boundaries around fields and binary file chunks. But when you switch to $.ajax(), jQuery tries to serialize everything as a query string by default—ruining binaries.
Why bother with AJAX here? Page doesn’t reload. Users stay on your site, maybe see a progress bar. And yeah, it works in older browsers like Safari 5, as long as you use the FormData API, introduced around then.
The key? FormData objects handle multipart/form-data natively. Append your file like formData.append('file', fileObject), set those crucial flags, and PHP picks it up seamlessly in $_FILES. No more empty arrays.
Why Your Current Code Fails
Look at your snippet:
$.ajax({
url: 'php/upload.php',
data: $('#file').attr('files'), // This is the culprit!
cache: false,
contentType: 'multipart/form-data',
processData: false,
type: 'POST',
success: function(data){
alert(data);
}
});
$('#file').attr('files')? That’s just a string—“undefined” or the attribute value. You need the DOM element’s .files property: $('#file')[0].files. Without it, no FileList reaches the server.
Even with processData: false, setting contentType: 'multipart/form-data' manually often breaks boundaries—jQuery can’t generate them right. PHP sees garbage, $_POST and $_FILES stay empty.
Common gotchas like this trip up jQuery AJAX file uploads all the time. Stack Overflow developers nailed it: let the browser handle content types via FormData.
And Safari 5? It supports this, but quirks like zero-byte files pop up if you mess up the append.
Creating FormData for jQuery AJAX File Uploads
FormData is your multipart/form-data savior. It’s a web API that bundles fields and files into the right format, no manual boundary strings needed.
Start simple. Grab the file:
var fileInput = $('#file')[0];
var file = fileInput.files[0]; // First file from FileList
if (!file) {
alert('No file selected!');
return;
}
var formData = new FormData();
formData.append('file', file); // 'file' matches PHP $_FILES key
formData.append('username', 'john'); // Extra fields? Easy.
Then fire the AJAX:
$.ajax({
url: 'php/upload.php',
type: 'POST',
data: formData,
contentType: false, // Critical: let browser set multipart boundaries
processData: false, // Don't stringify the FormData
cache: false,
success: function(response) {
alert('Upload success: ' + response);
},
error: function() {
alert('Upload failed!');
}
});
MDN docs explain it perfectly: contentType: false tells jQuery hands-off, browser adds multipart/form-data; boundary=----WebKitFormBoundaryABC123.
Test it. Select a file, hit upload—PHP gets the goods.
Complete jQuery AJAX Code Example
Here’s a full, working HTML/JS setup. Drop this in a page.
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
<input type="file" id="file" accept="image/*">
<button id="upload">Upload</button>
<div id="status"></div>
<script>
$('#upload').click(function() {
var fileInput = $('#file')[0];
var file = fileInput.files[0];
if (!file) return alert('Pick a file first.');
var formData = new FormData();
formData.append('file', file);
formData.append('description', 'My cool image');
$('#status').text('Uploading...');
$.ajax({
url: 'php/upload.php',
type: 'POST',
data: formData,
contentType: false,
processData: false,
cache: false,
xhr: function() {
var xhr = new window.XMLHttpRequest();
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
var percent = (e.loaded / e.total) * 100;
$('#status').text('Progress: ' + percent.toFixed(0) + '%');
}
});
return xhr;
},
success: function(data) {
$('#status').html('Success: ' + data);
},
error: function(xhr, status, error) {
$('#status').text('Error: ' + error);
}
});
});
</script>
</body>
</html>
Progress bar? Bonus from XHR2, native in Safari 5+. Cleans up your jQuery AJAX file upload flow.
PHP Server-Side Handling for Uploads
PHP shines here—no special extensions needed. Just check $_FILES.
<?php
// upload.php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
$file = $_FILES['file'];
// Validate basics
if ($file['error'] !== UPLOAD_ERR_OK) {
die('Upload error: ' . $file['error']);
}
$allowedTypes = ['image/jpeg', 'image/png'];
if (!in_array($file['type'], $allowedTypes)) {
die('Invalid file type.');
}
if ($file['size'] > 5 * 1024 * 1024) { // 5MB limit
die('File too large.');
}
$uploadDir = 'uploads/';
if (!file_exists($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
$fileName = basename($file['name']);
$targetPath = $uploadDir . time() . '_' . $fileName;
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
echo json_encode([
'success' => true,
'path' => $targetPath,
'description' => $_POST['description'] ?? 'No desc'
]);
} else {
die('Move failed.');
}
} else {
die('No file received.');
}
?>
Grab $_POST['description'] too—FormData handles mixed data effortlessly. CodexWorld tutorial adds MySQL logging if you want persistence.
Pro tip: Always move_uploaded_file(), never file_put_contents on $_FILES['tmp_name'].
Browser Compatibility for Safari 5 and Beyond
Safari 5 (2010!) supports FormData and XHR2 file uploads. Firefox 4+, Chrome 7+ too. But test:
- Safari 5.1+: Full support, though early versions might report 0-byte files occasionally.
- IE10+: Good.
- Older? Fallback to iframe tricks, but your spec says Safari 5 minimum.
Check support:
if (!window.FormData) {
alert('Browser too old for AJAX uploads.');
}
Stack Overflow thread confirms: !!window.FormData is reliable. No polyfills needed for your targets.
Advanced Tips: Multiple Files and Troubleshooting
Multiple files? Easy:
for (var i = 0; i < fileInput.files.length; i++) {
formData.append('files[]', fileInput.files[i]);
}
PHP loops $_FILES['files']. Set <input type="file" multiple>.
Troubleshooting:
- Empty
$_FILES? Double-checkcontentType: false. - 413 errors? PHP
upload_max_filesizein php.ini. - CORS? Add headers if cross-origin.
- Digipiph blog suggests
async: falsefor older jQuery, but modern ones handle it.
Validation client-side too: file.size, file.type. Secure uploads beat surprises.
Big files? Chunking via File.slice() for resumables, but that’s next level.
Sources
- Sending multipart/formdata with jQuery.ajax — Comprehensive Q&A on FormData usage and PHP handling: https://stackoverflow.com/questions/5392344/sending-multipart-formdata-with-jquery-ajax
- Using FormData Objects — MDN guide to FormData API with AJAX examples: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Using_FormData_Objects
- AJAX File Upload with FormData, jQuery, PHP & MySQL — Step-by-step tutorial with validation: https://www.codexworld.com/ajax-file-upload-with-form-data-jquery-php-mysql/
- Submitting multipart/form-data using jQuery and Ajax — Practical form serialization tips: https://digipiph.com/blog/submitting-multipartform-data-using-jquery-and-ajax
- How can I check if the browser support HTML5 file upload / FormData object? — Browser compatibility detection: https://stackoverflow.com/questions/7296426/how-can-i-check-if-the-browser-support-html5-file-upload-formdata-object
Conclusion
Switch to FormData with contentType: false and processData: false, grab files from [0].files, and PHP’s $_FILES will populate—no more empty POST headaches in your jQuery AJAX file upload setup. This scales to multiples, adds progress bars, and runs smooth on Safari 5+. Test thoroughly, validate everything, and you’re golden for reliable multipart/form-data handling.
Use new FormData() to handle multipart/form-data uploads in jQuery AJAX, appending files from $('#file')[0].files. Set contentType: false and processData: false to avoid boundary issues and string conversion errors. On PHP, access via $_FILES['file-0'] or use file[] for arrays. This fixes empty $_POST in file uploads, works in Safari 5+, and supports multiple files with name="file[] multiple". For older browsers, emulate with custom XHR. Serialize entire forms with new FormData(form) for simplicity.
Create a FormData object, append files like formData.append('file', $('#fileinput')[0].files[0]), and send via jQuery AJAX with processData: false and contentType: false for automatic multipart/form-data boundaries. PHP receives files in $_FILES['file']. Avoid manual Content-Type headers to prevent errors in file uploads. Compatible with Safari 5 and modern browsers; use Fetch API alternative for async handling with extra fields.
Bundle files and form fields in FormData (e.g., formData.append('file', $('#file')[0].files[0])), then POST via jQuery AJAX with contentType: false and processData: false. PHP processes $_POST for fields and $_FILES for uploads, validating types/sizes before move_uploaded_file(). Supports Safari 5/FF/Chrome; includes MySQL storage, JSON responses, and fixes for empty POST in PHP file upload scenarios.
Submit multipart/form-data forms via AJAX using new FormData($(this)[0]) on form submit, with contentType: false, processData: false, and async: false for reliability. Handles multiple files (e.g., profileImg[]) and fields; PHP accesses via $_POST and $_FILES['profileImg']. Simple fix for jQuery AJAX file uploads, works across browsers including Safari 5, preventing issues like empty arrays.
Check FormData support with !!window.FormData or XHR upload progress for AJAX file compatibility. Safari 5.1.7+ supports but may report file size 0; FF<6 and Opera<12 buggy. Use for jQuery AJAX multipart/form-data uploads to ensure PHP file upload works; fallback emulation needed for older browsers.