ThinkPHP 3導出數據防止內存泄露

1、php代碼

/**
    * 分頁導出數據(老是順序導出).
    * 返回的是 json, 由前端 js 生成 csv/xls 文件.
    * @param int $page 當前頁數
    * @param int $limit 當前導出的記錄數目 (這個值不能太大, 不然會形成內存超出限制)
    */
   public function export_json($page = 1, $limit = 2000)
   {
      $this->checkIndexPermission();
      if (!$this->admin->canExportGoods()) {
         exit;
      }
      
      $exportType = null;
      $goods = D('Goods');
      $where = $this->getSearchOptions();
      $count = $goods->alias('a')->where($where)->count();
      $totalPage = ceil($count / $limit);
      $list = $goods->alias('a')
         ->join('LEFT JOIN goods_trace b ON a.id = b.gid')
         ->field('a.*,b.record_time')
         ->where($where)
         ->page($page, $limit)->order('a.id desc')->select();
      $titles = $this->getExportFieldNames($exportType);
      $rows = $this->exportToArray($list);
      $data = array(
         'titles' => array_values($titles), // 標題
         'rows' => $rows, // 數據列表
         'percentage' => (int)(($page / $totalPage) * 100), // 當前總進度
      );

      // 若是不是最後一頁, 就加一個 next_url 的字段, 前端根據這個字段繼續請求下一頁數據
      if ($page < $totalPage) {
         $search = I('get.');
         $search['page'] = $page + 1;
         $data['next_url'] = U('export_json', $search);
      }
      $this->ajaxReturn($data);
   }
複製代碼
/***
    *  導出字段名稱
    */
   public function getExportFieldNames()
   {
      $allFields = array(
         // 字段名 => 標題名稱
         'id' => '訂單id',
         'dian' => '店鋪',
         'utime' => '進店時間',
         'wang' => '網銷',
         'name' => '客戶姓名',
         'phone' => '客戶電話',
         'name1' => '客戶姓名1',
         'phone1' => '客戶電話1',
         'wx' => '微信',
         'is_contacted' => '是否聯繫',
         'men' => '門市',
         'record_time' => '走單時間',
         'jiage' => '標準套系',
         'sfje' => '實付金額',
         'kefu' => '客服',
         'beizhu' => '備註',
         'ctime' => '錄入時間',
      );

      return $allFields;
   }
複製代碼
/**
    * 獲取 字段映射 數組.
    * [數據庫中的字段名 => 導出的字段名]
    * @return array
    */
   public function exportToArray($list)
   {
      $res = array();
      foreach ($list as $key=>$data) {
         $res[$key] = array_values(array(
            'id' => $data['id'],
            'dian' => $data['dian'],
            'utime' => $data['utime'],
            'wang' => $data['wang'],
            'name' => $data['name'],
            'phone' => $data['phone'],
            'name1' => $data['name1'],
            'phone1' => $data['phone1'],
            'wx' => $data['wx'],
            'is_contacted' => $data['is_contacted'] == 1 ? '是' : '否',
            'men' => $data['men'],
            'record_time' => $data['record_time'],
            'jiage' => $data['jiage'],
            'sfje' => $data['sfje'],
            'kefu' => $data['kefu'],
            'beizhu' => $data['beizhu'],
            'ctime' => $data['ctime'],
         ));
      }
      return $res;
   }

複製代碼

2、html代碼

<a id="export-data-btn" class="btn btn-warning" data-href="{:U('keyun/list/export_json', array_merge($search_options))}">數據導出</a>

<div class="modal fade" id="export-modal" tabindex="-1" role="dialog" data-backdrop="static">
   <div class="modal-dialog" role="document">
      <div class="modal-content">
         <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
            <span aria-hidden="true">&times;</span></button>
            <h4 class="modal-title" id="myModalLabel">數據導出</h4>
         </div>
         <div class="modal-body">
            <p>數據導出中, 請勿刷新或關閉瀏覽器</p>
            <div class="progress">
               <div id="export-progress" class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0"
                    aria-valuemax="100" style="width: 0%;">
               </div>
            </div>
         </div>
      </div>
   </div>
</div>

<script>
   $(function () {
      $('#export-data-btn').click(function () {
         var url = $(this).data('href');
         var fetched_data = {
            rows: []
         };

         var hint = $('#export-modal');
         hint.modal('show');
         fetchDataAndExport(url, fetched_data);

         // 分頁獲取數據, 並導出
         function fetchDataAndExport(url, data, failed) {
            if (!failed) {
               failed = 0;
            }
            $.get(url).done(function (res) {
               if (res.titles) {
                  data.titles = res.titles
               }
               if (res.rows) {
                  data.rows = $.merge(data.rows, res.rows);
               }
               if (res.next_url) { // 尚未完成, 繼續獲取數據
                  fetchDataAndExport(res.next_url, data, failed)
               } else { // 已經完成
                  var date = new Date();
                  var datetime = date.getFullYear() + '-' + date.getMonth() + '-' + date.getDate();
                  download(generateCSV(data), datetime + '.xls', 'application/vnd.ms-excel')
                  setTimeout(function () {
                     hint.modal('hide')
                  }, 1000)
               }

               $('#export-progress').css('width', res.percentage + '%');
            }).fail(function () {
               failed++;

               // 整個過程容許失敗 5次, 若是超過 5次, 終止
               if (failed > 5) {
                  hint.modal('hide');
                  alert('導出失敗, 獲取數據失敗!')
               } else {
                  // 重試
                  fetchDataAndExport(url, data, failed)
               }
            })
         }

         // 直接用 js 生成(保存)文件
         function download(data, filename, type) {
            var file = new Blob([data], {type: type});
            // IE10+
            if (window.navigator.msSaveOrOpenBlob) {
               window.navigator.msSaveOrOpenBlob(file, filename);
            } else { // Others
               var a = document.createElement("a"),
                  url = URL.createObjectURL(file);
               a.href = url;
               a.download = filename;
               document.body.appendChild(a);
               a.click();
               setTimeout(function () {
                  document.body.removeChild(a);
                  window.URL.revokeObjectURL(url);
               }, 0);
            }
         }

         // 返回字符串
         function generateCSV(data) {
            var content = "\ufeff"; // BOM頭, 防止中文亂碼
            if (data.titles) {
               content += generateRow(data.titles);
            }
            for (var i in data.rows) {
               content += generateRow(data.rows[i])
            }

            return content
         }

         function generateRow(row) {
            var str = '';
            for (var i = 0; i < row.length; i++) {
               var val = row[i] === null ? '' : row[i];
               if (i === 0) {
                  str = val
               } else {
                  str = str + ',' + val
               }
            }

            return str + '\r\n';
         }
      });
   })
</script>

複製代碼
相關文章
相關標籤/搜索