Want to upload files without making the user watch the whole page reload like it is 2003 and patience is a virtue? This guide shows how to use Ajax with FormData and XMLHttpRequest to send files to a PHP backend while keeping progress feedback, validation and basic security in place. It is practical and mildly entertaining when servers behave themselves.
Keep the HTML tiny and polite. The browser does the heavy lifting for file picking. Hook into submit and prevent the annoying full page refresh.
<form id="uploadForm" enctype="multipart/form-data">
<input id="fileInput" name="file" type="file" />
<button type="submit">Upload</button>
</form>
<progress id="progress" max="100" value="0">0</progress>
<div id="status">Waiting for your file</div>
FormData is literally made for this. XMLHttpRequest gives progress events so you can show a progress bar and avoid angry support tickets.
document.getElementById('uploadForm').addEventListener('submit', function e
e.preventDefault()
var fileInput = document.getElementById('fileInput')
if ! fileInput.files.length
document.getElementById('status').textContent = 'No file selected'
return
var fd = new FormData()
fd.append('file', fileInput.files[0])
var xhr = new XMLHttpRequest()
xhr.open('POST', '/upload.php', true)
xhr.upload.addEventListener('progress', function ev
if ev.lengthComputable
var percent = Math.round(ev.loaded / ev.total * 100)
document.getElementById('progress').value = percent
document.getElementById('status').textContent = 'Uploading ' + percent + '%'
)
xhr.onreadystatechange = function
if xhr.readyState == 4
try
var res = JSON.parse(xhr.responseText)
if res.success
document.getElementById('status').textContent = 'Uploaded as ' + res.name
else
document.getElementById('status').textContent = 'Upload failed ' + res.message
catch err
document.getElementById('status').textContent = 'Unexpected server response'
xhr.send(fd)
)
The server must check for a file, validate it and then move it to a safe place. The following is minimal and focused on the important checks that stop curious exploits and confused users.
<?php
if isset($_FILES['file']) and $_FILES['file']['error'] == 0
$name = basename($_FILES['file']['name'])
$size = $_FILES['file']['size']
$tmp = $_FILES['file']['tmp_name']
if $size > 5 * 1024 * 1024
echo json_encode(array('success' => false, 'message' => 'File too large'))
exit
$finfo = finfo_open(FILEINFO_MIME_TYPE)
$mime = finfo_file($finfo, $tmp)
finfo_close($finfo)
$allowed = array('image/png', 'image/jpeg', 'application/pdf')
if ! in_array($mime, $allowed)
echo json_encode(array('success' => false, 'message' => 'Disallowed file type'))
exit
$safe = uniqid('up_', true) . '_' . preg_replace('/[^a-zA-Z0-9._-]/', '_', $name)
$dest = __DIR__ . '/uploads/' . $safe
if move_uploaded_file($tmp, $dest)
echo json_encode(array('success' => true, 'name' => $safe, 'orig' => $name))
else
echo json_encode(array('success' => false, 'message' => 'Could not move file'))
else
echo json_encode(array('success' => false, 'message' => 'No file received'))
?>
Flow recap in plain human language. The browser builds FormData with the file and sends it over XMLHttpRequest. The server checks size and mime type then moves the file to a controlled location and responds with JSON. The client reads that JSON and updates the UI and progress bar. If something goes wrong the user sees a useful message not a cryptic 500 page.
Pro tip Give each saved file a generated unique name and keep the original name in your database. That avoids accidental overwrites and keeps an audit trail if you need to explain why someone uploaded a 23 megabyte cat photo to an expense form.
There you go. A tidy Ajax upload with FormData, progress UI, and server side checks so your uploads behave like adults.
I know how you can get Azure Certified, Google Cloud Certified and AWS Certified. It's a cool certification exam simulator site called certificationexams.pro. Check it out, and tell them Cameron sent ya!
This is a dedicated watch page for a single video.