When uploading a file via AJAX with PhP, one generally does the following (I’m new to this so please tell me if there is a better way to do this):
XMLHttpRequest
, e.g. upload_form.phpIs it possible to abort the XMLHttpRequest from within the external upload.php file?.
The reason for wanting to do this is that if the file already exists on the server, I'd like to abort the XMLHttpRequest
rather than making the user wait until the upload has completed to be informed of the failure.
I throttled the speed in the network tab in order to exaggerate the problem.
The user uploads a file that DOES NOT exist on the server in the uploads directory:
The user uploads a file that DOES exist on the server in the uploads directory:
upload_form.php:
<!-- enctype and method added on suggestions from previous question -->
<form class="form" id="upload_form" enctype="multipart/form-data" method="POST">
<input type="file" name="file_to_upload" id="file_to_upload"><br>
<input class="button" type="submit" value="Upload">
</form>
<script>
const upload_form = document.getElementById('upload_form');
var file_to_upload = document.getElementById('file_to_upload');
upload_form.addEventListener("submit", upload_file);
function upload_file (e) {
e.preventDefault();
const xhr = new XMLHttpRequest()
xhr.open("POST", "upload.php");
xhr.upload.addEventListener("progress", e => {
const percent = e.lengthComputable ? (e.loaded / e.total) * 100 : 0;
console.log(percent.toFixed(0) + "%");
});
// ================= MAIN PART OF QUESTION ===================
// Can I force this to fire from upload.php if file exists?
xhr.addEventListener("abort", e => {
console.log(e);
});
// ===========================================================
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// update some response area here
}
};
xhr.send(new FormData(upload_form));
}
</script>
upload.php:
<?php
$target_path = "uploads/".basename($_FILES["file_to_upload"]["name"]);
$uploaded_file = $_FILES['file_to_upload']['tmp_name'];
$upload_ok = true;
$errors = [];
// ================= MAIN PART OF QUESTION ==========================
// I want this to cause the abort event to fire on the XMLHttpRequest
if (file_exists($target_path)) {
$upload_ok = false;
array_push($errors, 'The file already exists on the server. Please rename the file and try again.');
}
// ==================================================================
if(!$upload_ok) {
echo 'The file was not uploaded because:<br>';
foreach ($errors as $err) {
echo $err.'<br>';
}
} else {
if(move_uploaded_file($_FILES["file_to_upload"]["tmp_name"], $target_path)) {
echo 'File uploaded successfully';
} else {
echo 'Something went wrong. Please try again.';
}
}
?>
I've tried checking different combinations of readyState
and status
in upload_form.php but this hasn't helped.
By the time the PHP code is get to begin, the file has been fully uploaded so there is no point aborting it that way, which would be to simply return the message. Instead, you can make a prior to upload AJAX call and check if the file exists and only if it doesn't then upload the file or else message the user about it.
exists.php
with the following code<?php
$target_path = "uploads/".$_GET['file'];
$file_exists = file_exists($target_path);
header('Content-Type: application/json');
// Return a JSON object with a single boolean property to indicate if the file exists
echo json_encode(['exists' => $file_exists]);
exists.php
resultfunction check_file_exists() {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", `exists.php?file=${encodeURIComponent(file_to_upload.files[0].name)}`);
xhr.responseType = 'json';
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
resolve(this.response && this.response.exists);
}
};
xhr.send();
});
}
upload_file
submit function to first call the check_file_exists
promise// An element to display a message to the user
const message = document.getElementById('message');
function upload_file(e) {
e.preventDefault();
message.textContent = '';
// Call the promise to check if the selected file exists on the server
check_file_exists().then(exists => {
if(exists) {
// If it exists message the user about it and do nothing else
message.textContent = `File "${file_to_upload.files[0].name} already exists`;
} else {
// If it does not exists upload the file
const xhr = new XMLHttpRequest();
xhr.open("POST", "upload.php");
xhr.upload.addEventListener("progress", e => {
const percent = e.lengthComputable ? (e.loaded / e.total) * 100 : 0;
console.log(percent.toFixed(0) + "%");
});
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// update some response area here
message.textContent = this.responseText;
}
};
xhr.send(new FormData(upload_form));
}
});
}