简体   繁体   中英

How to abort XMLHttpRequest.upload from the external script?

As a follow up to Why is $_FILES[] always empty? :

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):

  • Have one file with a form and javascript where you setup and send your XMLHttpRequest , eg upload_form.php
  • Have another, external php file to process the upload request, eg upload.php

My question is this:

Is 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.


Some images to illustrate the problem:

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:

失败

Some code to illustrate the problem:

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.

Create a simple 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]);

Create a promise to make a XHR call and get the exists.php result

function 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();
  });
}

Refactor the 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));
    }
  });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM