检测 "php://output" 的完成情况并使用 window.open 打开

3
我使用PhpSpreadsheet来导出MySQL数据,具体步骤如下: 第一页: 点击会触发window.open函数打开一个包含导出脚本的页面。
window.open('/export.php?data.., '_self');

第二页(export.php):

导出和php://output的整个系统

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

我能否知道导出页面是否已经完成呢?这是为了触发一个覆盖层。

我尝试了什么?

在stackoverflow上,我尝试了以下解决方案,但它并没有起作用:

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个回答

1
这将使用jQuery执行一个ajax请求,结果是下载文件而不刷新页面。
<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();
});

确保你的PHP输出正确的内容类型

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;
}

另一种选择

根据这篇文章,并没有一个可用的api可以真正检测到所有不同浏览器中javascript窗口对象的加载。该方法使用延迟回调和postMessage方法来适应大多数现代浏览器。

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')
});

只有在我手动关闭窗口并且不使用_self时,该方法才有效。你知道在$objWriter->save('php://output');之后关闭窗口的方法吗?因为我尝试使用ob_clean();echo "<script>window.close();</script>";来关闭它,但是都没有起作用。 - Simone Rossaini
起初,我只专注于使用开放窗口和检测加载完成。现在我在问自己为什么要用这种方法?为什么不用异步获取或者ajax请求呢? - Kinglish
我尝试了你的方法,但当按下按钮后,“/export.php?” 后面的所有数据都被删除了。 - Simone Rossaini
好的,更新了一个更简单的方法。在隐藏表单中定义表单字段,并通过jQuery提交。PHP输出头部也包括在内,但你需要自定义它们。 - Kinglish
使用php://output时,不存在一个$file,那么如何解决主要问题呢?我如何检测脚本的结束?因为如果你使用表单,它与使用open window是一样的。 - Simone Rossaini
显示剩余2条评论

0

我会给@Kinglish的答案点赞,因为awaitLoad函数可以帮助解决问题,在我的情况下,我使用了ajax代替这种方法链接答案

这是关于什么的?

  • 创建一个带有json类型的Ajax调用,以导出页面,并使用done来捕获JSON,如下所示:

    overlay.style.display = "block"; //激活覆盖层
    $.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"; //取消激活覆盖层
    });
    
  • 在PHP页面中使用ob_get_contents捕获php://output,然后返回json,如下所示:

    $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));
    
    
  • 最后一步是创建、点击和删除一个虚假链接。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接