【HTML5版】導出Table數據並保存為Excel


首發我的博客 http://blog.meathill.com/tech/js/export-table-data-into-a-excel-file.html

最近接到這么個需求,要把<table>顯示的數據導出成Excel表。類似的需求並不稀罕,過去我通常用PHP輸出.csv文件,不過這次似乎不能這么做:數據源表格允許用戶篩選和排序,與原始數據表有區別,而傳遞操作又比較麻煩;另外.csv文件的功能受限嚴重,難以擴展。所以我准備嘗試下別的做法。

Google之,發現HTML5又成了一座分水嶺。之前在IE瀏覽器下,用戶可以利用ActiveXObject創建Excel.application對象來處理——當然不兼容Mac。后來Excel開放標准,可以導出xml格式的文件,dataURI就有了用武之地,導出<table>數據並保存為Excel有了更好的選擇。

(以下內容與StackOverflow中的答案有重合,那個3條贊同的我認為是最佳答案,可惜我沒法頂他……)

准備工作

  1. 創建一個空白的Excel文檔
  2. 另存為“XML表格”,xml格式
  3. 好了,模版搞定

圖省事兒的也可以直接使用我的模板(這一段我使用了Handlebars,以便將來填充數據)

template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40">\
  <head><!--[if gte mso 9]>\
    <xml>\
      <x:ExcelWorkbook>\
        <x:ExcelWorksheets>\
          <x:ExcelWorksheet>\
            <x:Name>{{worksheet}}</x:Name>\
            <x:WorksheetOptions>\
              <x:DisplayGridlines/>\
            </x:WorksheetOptions>\
          </x:ExcelWorksheet>\
        </x:ExcelWorksheets>\
      </x:ExcelWorkbook>\
    </xml><![endif]-->\
  </head>\
  <body>\
    {{#each tables}}<table>{{{this}}}</table>{{/each}}\
  </body>\
</html>';

復制表格數據

復制數據比較簡單了。如前面模版所示,這里我很野蠻的直接復制theadtbody的全部代碼,填充內容。當然為了體現用戶操作,我只復制顯示的tr。這里需要注意的是,jQuery判斷一個dom是否處於顯示狀體基於以下3點:

  1. display:none
  2. 表單元素,type="hidden"
  3. 寬高為0
  4. 父級以上節點不顯示,自己也不會顯示

所以,不能先clone()find(':hidden').remove(),因為沒添加到主Dom樹的節點寬高都是0,也就會被認為還沒顯示,這下就都干掉了。

輸出內容

套用模版之后,我們就有了完整的表格數據。接下來,我們需要把其轉換成base64格式,以便套用dataURI輸出。於是便要使用btoa這個函數(將二進制數據轉換成base64格式的字符串,HTML5的大禮之一,操作二進制的API),不過注意,這個函數不能直接轉換普通unicode字符,不然大多數瀏覽器都會拋出異常。所以需要先經過兩步轉換:

function base64(string) {
  return window.btoa(unescape(encodeURIComponent(string)));
}

(MDN中還推薦了另外一種做法,通過Typed Array做中介,我沒有實操,有興趣的可以試下)

然后配上base64頭和mime類型,就可以觸發下載了:

var uri = 'data:application/vnd.ms-excel;base64,';
location.href = uri + base64(template(tables));

提升體驗

貌似到這里就完成了,不過作為一名掛職產品總監的碼農,我很難容忍下載的文件文件名是“下載”,而且還沒有擴展名(Windows 8下沒有;Windows 7 和 Mac下會有.xls的擴展名,我認為和已裝軟件注冊過的mime類型有關)。

這是個用在內部管理后台的需求,我之前曾要求大家必須使用Chrome訪問后台;而且我知道,Chrome已經支持<a>里的download屬性。那么這就好辦了,因為onclick事件會先於系統默認行為觸發,所以我可以在這個事件的處理函數中將生成的Base64放在被點擊按鈕的href里,並將其download屬性設為容易理解的“某年某月末日至某年某月某日廣告數據分析.xls”。至此,此項功能宣告圓滿。

HTML部分(使用到Bootstrap和Handlebars):

<a href="#" title="點擊下載" class="btn btn-primary export-button" download="{{start}}至{{end}}廣告數據分析.xls"><i class="icon-download-alt icon-white"></i> 導出</a>

JavaScript部分

tableToExcel: function (tableList, name) {
  var tables = [],
      uri = 'data:application/vnd.ms-excel;base64,',
      template = Handlebars.compile('<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{{worksheet}}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body>{{#each tables}}<table>{{{this}}}</table>{{/each}}</body></html>');

  for (var i = 0; i < tableList.length; i++) {
    tables.push(tableList[i].innerHTML);
  }
  var data = {
    worksheet: name || 'Worksheet',
    tables: tables
  };
  return uri + base64(template(data));
},
exportHandler: function (event) {
  var tables = this.$('table'),
      table = null;
  tables.each(function (i) {
    var t = $('<table><thead></thead><tbody></tobdy></table>');
    t.find('thead').html(this.tHead.innerHTML);
    t.find('tbody').append($(this.tBodies).children(':visible').clone());
    t.find('.not-print').remove(); // not-print 是@media print中不會打印的部分
    t.find('a').replaceWith(function (i) { // 表格中不再需要的超鏈接也移除了
      return this.innerHTML;
    });
    table = table ? table.add(t) : t;
  });
  event.currentTarget.href = Dianjoy.utils.tableToExcel(table, '廣告數據');
}

尾聲

說是圓滿,其實也不盡然,因為URL有2M的長度限制,遇到真正的大表仍然可能出問題(我沒實測)。

最后例行吐槽:老板(領導)想提升工作效率,光逼員工沒啥意義,必須關注員工日常使用的軟件:不許用亂七八糟的瀏覽器,統一Chrome;360一率禁用(最近遇到N起升級Chrome Dev 30版導致各種bug的問題);全部裝Windows 8(自帶殺毒,幾乎所有外設秒配)。能做到這幾點,公司辦公效率提升1倍不止。

再多說兩句:我們對外的后台雖然做到了基本兼容,但如果用戶使用非Chrome訪問,仍然會建議他換用Chrome。目前Chrome訪問占比已經上升到90%,IE678不到5%,希望不久的將來,我們的用戶都能盡情享受HTML5帶來的優秀體驗,我們的開發成本也能降得更低。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM