Detect finish of "php://output" with window.open

Viewed 199

I use PhpSpreadsheet for export mysql data in this way:

Page 1:

button trigger window.open to page with script for export

window.open('/export.php?data.., '_self');

Page 2 (export.php):

Whole system for export and php://output

ob_end_clean();
ob_start();
$objWriter->save('php://output');            
exit;

Can I somehow understand if the export page has finished? I need this for trigger a overlay.

What i have tried?

Looking in stackoverflow I tried this solution but it didn't work:

overlay.style.display = "block";
let myPopup =  window.open('/export.php?data.., '_self');
myPopup.addEventListener('load', () => {
   console.log('load'); //just for debug
   overlay.style.display = "none";
}, false);
2 Answers

This will execute an ajax request which will result in a download without refreshing the page with jQuery

<button id='download'>Download Spreadsheet</button>
<form method='get' action='/export.php?' id='hiddenForm'>
  <input type='hidden' name='foo' value='bar' />
  <input type='hidden' name='foo2' value='bar2' />
</form>


$(document).on('click', '#download', function (e) {
    e.preventDefault();
    $('#hiddenForm').submit();
});

Make sure your PHP outputs the correct content type

if (file_exists($file)) {
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="'.basename($file).'"');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file));
    readfile($file);
    exit;
}

Another Option

Based on this post, there isn't an api available to truly detect the loading of a javascript window object across all different browsers. This method uses a defer callback and postMessage approach to acommodate most modern browsers.

function defer (callback) {
    var channel = new MessageChannel();
    channel.port1.onmessage = function (e) {
        callback();
    };
    channel.port2.postMessage(null);
}

var awaitLoad = function (win, cb){
    var wasCalled = false;
    function unloadListener(){
        if (wasCalled)
            return;
        wasCalled = true;
        win.removeEventListener("unload", unloadListener);
        win.removeEventListener("pagehide", unloadListener);
        // Firefox keeps window event listeners for multiple page loads
        defer(function (){
            win.document.readyState;
            // IE sometimes throws security error if not accessed 2 times
            if (win.document.readyState === "loading")
                win.addEventListener("load", function loadListener(){
                    win.removeEventListener("load", loadListener);
                    cb();
                });
            else
                cb();
        });
    };
    win.addEventListener("unload", unloadListener);
    win.addEventListener("pagehide", unloadListener);
    // Safari does not support unload
}


w = window.open();
w.location.href="/export.php?data=foo";
awaitLoad(w, function (){
   console.log('got it')
});

I will give +1 to @Kinglish answer because awaitLoad function can help, in my case i used ajax instead with this method Link answer

What is it about?

  • Create an Ajax call with json type to export page and use done to catch JSON like:

    overlay.style.display = "block"; //active overlay
    $.ajax({
      url: "/export.php",
      type: "GET",
      dataType: 'json',
      data: {
        data: data
      }
    }).done(function(data) {
      var $a = $("<a>");
      $a.attr("href", data.file);
      $("body").append($a);
      $a.attr("download", "nameoffile.xls");
      $a[0].click();
      $a.remove();
      overlay.style.display = "none"; //deactive overlay
    });
    
  • In PHP page catch php://output with ob_get_contents then return json like:

    $xlsData = ob_get_contents();
    ob_end_clean();
    $response = array(
      'op' => 'ok',
      'nomefile' => 'nameofile',
      'file' => "data:application/vnd.ms-excel;base64,".base64_encode($xlsData)
    );
    die(json_encode($response));
    
    
  • The last step is create, click and remove a fake link.

Related