1. 程式人生 > >使用Ajax實現非同步匯出資料到excel

使用Ajax實現非同步匯出資料到excel

最近做專案的時候就遇到需要把使用者分散在多個數據表中的資料整合到一起既可以線上檢視,又能匯出excel。對於這個需求,如果使用者量小的情況下直接點選連結跳轉請求伺服器處理從資料表中取出資料生成excel再返回客戶端進行開啟下載操作即可。但是當用戶非常多,資料非常大的時候伺服器處理請求的所需要的時間就會越長,當超出客戶端獲取響應的等待時間就會返回“502 Bad GateWay”這樣的超時反饋。這顯然不是我們想要。那麼我們需要在處理資料的過程中持續給客戶端反饋,以避免出現超時連線的情況。這時候就考慮用ajax非同步處理資料讀取過程,然後再通過window.location.href=regionURL;

來訪問檔案下載連結進行excel下載。(注意:ajax請求只是個“字元型”的請求,即請求的內容是以文字型別存放的。檔案的下載是以二進位制形式進行的,所以ajax無法處理二進位制流的response來下載檔案

首先我們來看後臺程式碼實現:

if ($_GET['exporttype'] == 'excel'){
            if(!isset($_GET['id']) && !isset($_GET['end']))//id是指使用者表的id,用來定位最後一個取出的資料,以此為標準取下一個1000條資料
                return false;

            if
(isset($_GET['end'])){//end表示資料已經全部準備完畢可以下載excel import('libraries.excel'); $excel_obj = new Excel(); $excel_data = $_SESSION['excel_data']; //設定樣式 $excel_obj->setStyle(array('id'=>'s_title','Font'=>array('FontName'
=>'宋體','Size'=>'12','Bold'=>'1'))); $excel_data = $excel_obj->charset($excel_data,CHARSET); $excel_obj->addArray($excel_data); $excel_obj->addWorksheet($excel_obj->charset('table1',CHARSET)); $file_name = $excel_obj->charset('使用者行為資料統計',CHARSET).date('Y-m-d-H',time()); $excel_obj->generateXML($file_name);//生成excel檔案,並使用header()函式來將它交付給瀏覽器。 unset ($_SESSION['excel_data']);return;//銷燬session } //匯出Excel if($_GET['id'] == 0){ $excel_data = array(); //header foreach ($statlist['headertitle'] as $v){ $excel_data[0][] = array('styleid'=>'s_title','data'=>$v);//設定表頭 } Session_start(); $_SESSION['excel_data']=$excel_data;//用session儲存取出的資料,防止ajax請求的時候把上一次請求的資料重新整理掉 }else{ $excel_data = $_SESSION['excel_data']; } //data $data = $model->getUserDataList($_GET['id'], 1000); if(!empty($data)){ $pagenum = 1000*$_GET['page']; foreach ($data as $k=>$v){ $k = $pagenum + $k; $excel_data[$k+1][] = array('data'=>$v['user']); $excel_data[$k+1][] = array('data'=>$v['phone']); $excel_data[$k+1][] = array('data'=>$v['contacts']); $excel_data[$k+1][] = array('data'=>$v['email']); $excel_data[$k+1][] = array('data'=>$v['wechat_no']); $excel_data[$k+1][] = array('data'=>$v['is_bind']); $excel_data[$k+1][] = array('data'=>$this->getAreaInfo($v['area_id']));//所在地 $excel_data[$k+1][] = array('data'=>$this->getIndustryInfoList($v['id']));//行業方向 $excel_data[$k+1][] = array('data'=>$this->getTagInfoList($v['id']));//技術方向 $excel_data[$k+1][] = array('data'=>$v['login_number']); $excel_data[$k+1][] = array('data'=>$v['pc_login']); $excel_data[$k+1][] = array('data'=>$v['wx_login']); $excel_data[$k+1][] = array('data'=>date('Y-m-d-H', $v['last_time'])); $excel_data[$k+1][] = array('data'=>date('Y-m-d-H', $v['time'])); $excel_data[$k+1][] = array('data'=>$v['user_authentication_type']); $excel_data[$k+1][] = array('data'=>$v['enterprise_name']); $excel_data[$k+1][] = array('data'=>$v['is_store']); $excel_data[$k+1][] = array('data'=>$v['title']); $excel_data[$k+1][] = array('data'=>$v['need_count']); $excel_data[$k+1][] = array('data'=>$v['t_count']); $excel_data[$k+1][] = array('data'=>$v['goods_count']); } $last = array_pop($data); $id = $last['id']; $_SESSION['excel_data']=$excel_data; die(json_encode(array('id'=>$id,'msg'=>$excel_data))); }else{ die(json_encode(array('msg'=>'success'))); } }

前臺Ajax程式碼

<a class="btns" href="javascript:void(0);" id="export_btn"><span>匯出Excel</span></a>
<script>
$(function () {

    //匯出圖表
    $("#export_btn").click(function(){
        ajaxFile();
    });
});

function ajaxFile(id,page){
    var id = arguments[0] ? arguments[0] : 0;
    var page = arguments[1] ? arguments[1] : 0;

    $.ajax({
        url: "<?php echo $output['actionurl'];?>&exporttype=excel",
        type: 'GET',
        data: {
              id:id,
              page:page,
        },
        dataType:"json",
        success: function(obj){
            if(obj){
             if(obj.msg == "success"){
                 window.location.href="<?php echo $output['actionurl'];?>&exporttype=excel&end=1";
             }else{
                 ajaxFile(obj.id, ++page);
             }
            }else{
                alert("ERROR!");
            }
        }
     })
}
</script>

最後展示generateXML()方法是如何生成excel檔案,並使用header()函式來將它交付給瀏覽器。

/**
    * 生成excel檔案
    * 最後生成excel檔案,並使用header()函式來將它交付給瀏覽器。
    * @param string $filename 檔名稱
    */
    public function generateXML ($filename)
    {
        $encoded_filename = urlencode($filename);
        $encoded_filename = str_replace("+", "%20", $encoded_filename);
        //頭
        $ua = $_SERVER["HTTP_USER_AGENT"];
        header("Content-Type: application/vnd.ms-excel");
        if(preg_match("/MSIE/", $ua)){
            header('Content-Disposition: attachment; filename="'.$encoded_filename.'.xls"');
        }else if(preg_match("/Firefox/", $ua)){
            header('Content-Disposition: attachment; filename*="utf8\'\''.$filename.'.xls"');
        }else{
            header('Content-Disposition: attachment; filename="'.$filename.'.xls"');
        }
        header('Cache-Control: max-age=0');
        echo stripslashes ($this->header);
        //樣式
        echo "\n<Styles>";
        foreach((array)$this->cellstyle as $k=>$v){
            echo "\n".$v;
        }
        echo "\n</Styles>";
        //工作表
        echo implode ("\n", $this->worksheets);
        echo $this->footer;
    }

至此,就實現了使用ajax非同步匯出資料到excel的全部內容。我當時做的時候也是犯了“ajax不能下載檔案”的錯誤(為什麼不能下載呢,據百度解釋是由前端JS來操作處理IO流具有很大的安全隱患),將所有的請求都交由ajax去執行,導致執行到下載excel那一步就報錯了。除錯了一下午,才搞明白這個問題所在,實在是印象深刻,所以決定記下來,留待以後參考。有不同想法的小夥伴也可以跟我來交流呀,共同學習進步~~