【问题标题】:Detect finish of "php://output" with window.open使用 window.open 检测“php://output”的完成
【发布时间】:2021-06-16 09:47:47
【问题描述】:

我用PhpSpreadsheet这种方式导出mysql数据:

第 1 页:

button 触发 window.open 到带有导出脚本的页面

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

第 2 页(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);

【问题讨论】:

    标签: javascript php


    【解决方案1】:

    这将执行一个 ajax 请求,这将导致下载而不用 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();
    });
    

    确保您的 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;
    }
    

    另一种选择

    基于this post,没有可用于真正检测跨所有不同浏览器加载javascript 窗口对象的api。此方法使用延迟回调和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-&gt;save('php://output');之后关闭窗口的方法吗,因为我尝试使用ob_clean();和@987654329 @ 关闭它但没有任何效果。
    • 最初我只专注于使用打开的窗口和检测加载完成。现在我问自己为什么要使用这种方法?为什么不是异步获取或 ajax 请求?
    • 这实际上是一种在我进行调整的同时拥有一个加载器的方法,并且 ajax 请求将允许使用php://output ?
    • 我用另一个选项更新了我的答案,从 php://ouput 下载 ajax,无需重新加载页面
    • 我尝试了你的方法,但是当按下按钮时,/export.php? 之后的所有数据都被删除了
    【解决方案2】:

    我会给@Kinglish 答案+1,因为awaitLoad 函数可以提供帮助,在我的情况下,我使用ajax 而不是这个方法Link answer

    这是关于什么的?

    • 创建一个json类型的Ajax调用来导出页面并使用done捕获JSON 喜欢:

      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
      });
      
    • 在 PHP 页面中捕获 php://outputob_get_contents 然后返回 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));
      
      
    • 最后一步是创建、点击并删除虚假链接。

    【讨论】:

      猜你喜欢
      • 2019-06-17
      • 1970-01-01
      • 1970-01-01
      • 2018-06-04
      • 1970-01-01
      • 2018-03-25
      • 1970-01-01
      • 2023-03-04
      相关资源
      最近更新 更多