关键词搜索

全站搜索
×
密码登录在这里
×
注册会员

已有账号? 请点击

忘记密码

已有账号? 请点击

使用其他方式登录

纯前端vue如何实现10万+数据分批导出excel表格到xlsx文件方法

发布2024-02-05 浏览655次

详情内容

最近在做一个项目二次开发,有40万的用户数据需要导出到excel里,研究了几天,参考一些代码终于找到了实现的方法。如果数据有**10万条数据**,如何导出excel表格呢,这么大的数据如果一次导出对于服务器或者网络都是一个及大的考验,不太可能一性导出,要么资源被用完,要么网络超时,因为导出的数据excel文件可能有大几M或上百M,对于后端来说大量数据查询读取,特别是10万数据里如果涉及到其他表二次查询,基本上是卡死超时。


先说说后端PHP实现导出excel方式:无非用PhpOffice\PhpSpreadsheet组件来导出,但是对于大数据来说一次导出也是吃力。资源瓶颈基本是在于数据库查询或IO读取上。或者一次读取到内存里导致内存溢出异常,后端解决方案,无非是分批导出成多个excel,再多个excel打包成zip下载。


excel最大支持行数

在导出前先了解下一下`.xlsx`最多支持多少行及多少列数据。查了一下资料,

  • 在 Excel2003(.xls)文件:最大256(IV,2的8次方)列,最大65536(2的16次方)行;即横向256个单元格,竖向65536个单元格,不包括表头。

  • Excel2007(.xlsx)文件:最大16384(XFD,2的14次方)列,最大1048576(2的20次方)行(**即104万行**);即横向16384个单元格,竖向1048576个单元格。


纯前端vue导出10万+数据excel实现思路方法:

分批请求数据回浏览器

带分页码循环去后端接口请求分页数据。

分页数据在浏览器合并到变量里

用excel导出组件数据导出excel。


vue需要用的的组件。

  • file-saver

  • xlsx

  • script-loader


组件安装

  • npm install -S file-saver(生产依赖,则为-s)

  • npm install -S xlsx

  • npm install -D script-loader (开发依赖,则为-d)



vue10万大数据分批导出效果:

导出excel.gif

vue页面调用

import exportExcel from "@/utils/newToExcel.js";
//导出方法
async exports() {
      let [th, filekey, data, fileName] = [[], [], [], ""];
      let formValidate = this.artFrom;
      let excelData = {};
         
      excelData.page = 1;
      //分批请求数据接口
      for (let i = 0; i < excelData.page + 1; i++) {
        let lebData = await this.getExcelData(excelData);
        if (!fileName) fileName = lebData.filename;
        if (!filekey.length) {
          filekey = lebData.filekey;
        }
        if (!th.length) th = lebData.header;
        if (lebData.export.length) {
          data = data.concat(lebData.export);//分批数据合并
          excelData.page++;
        } else {
          //导出成excel
          exportExcel(th, filekey, fileName, data);
        }
      }
    },
    //请求数据接口
    getExcelData(excelData) {
      return new Promise((resolve, reject) => {
       //带分页数据接口
        storeProductApi(excelData).then((res) => {
          return resolve(res.data);
        });
      });
    },


Export2Excel.js 生成excel

//uihtm.com提供
import { export_json_to_excel } from '../vendor/Export2Excel';
/**
* @method exportExcel
* @param {Array} header   表头
* @param {Array} filterVal 表头属性字段
* @param {String} filename 文件名称
* @param {Array} tableData 列表数据
**/
export default function exportExcel(header, filterVal, filename, tableData) {
  var data = formatJson(filterVal, tableData);
  export_json_to_excel(
    header,
    data,
    filename
  );
}
function formatJson(filterVal, tableData) {
  return tableData.map(v => {
    return filterVal.map(j => {
      return v[j];
    });
  });
}

Export2Excel.js 导出excel工具方法

require('script-loader!file-saver');
require('script-loader!@/vendor/Blob');
require('script-loader!xlsx/dist/xlsx.core.min');
function generateArray(table) {
    var out = [];
    var rows = table.querySelectorAll('tr');
    var ranges = [];
    for (var R = 0; R < rows.length; ++R) {
        var outRow = [];
        var row = rows[R];
        var columns = row.querySelectorAll('td');
        for (var C = 0; C < columns.length; ++C) {
            var cell = columns[C];
            var colspan = cell.getAttribute('colspan');
            var rowspan = cell.getAttribute('rowspan');
            var cellValue = cell.innerText;
            if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
            //Skip ranges
            ranges.forEach(function (range) {
                if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
                    for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
                }
            });
            //Handle Row Span
            if (rowspan || colspan) {
                rowspan = rowspan || 1;
                colspan = colspan || 1;
                ranges.push({ s: { r: R, c: outRow.length }, e: { r: R + rowspan - 1, c: outRow.length + colspan - 1 } });
            }
            ;
            //Handle Value
            outRow.push(cellValue !== "" ? cellValue : null);
            //Handle Colspan
            if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
        }
        out.push(outRow);
    }
    return [out, ranges];
};
function datenum(v, date1904) {
    if (date1904) v += 1462;
    var epoch = Date.parse(v);
    return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}
function sheet_from_array_of_arrays(data, opts) {
    var ws = {};
    var range = { s: { c: 10000000, r: 10000000 }, e: { c: 0, r: 0 } };
    for (var R = 0; R != data.length; ++R) {
        for (var C = 0; C != data[R].length; ++C) {
            if (range.s.r > R) range.s.r = R;
            if (range.s.c > C) range.s.c = C;
            if (range.e.r < R) range.e.r = R;
            if (range.e.c < C) range.e.c = C;
            var cell = { v: data[R][C] };
            if (cell.v == null) continue;
            var cell_ref = XLSX.utils.encode_cell({ c: C, r: R });
            if (typeof cell.v === 'number') cell.t = 'n';
            else if (typeof cell.v === 'boolean') cell.t = 'b';
            else if (cell.v instanceof Date) {
                cell.t = 'n';
                cell.z = XLSX.SSF._table[14];
                cell.v = datenum(cell.v);
            }
            else cell.t = 's';
            ws[cell_ref] = cell;
        }
    }
    if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
    return ws;
}
function Workbook() {
    if (!(this instanceof Workbook)) return new Workbook();
    this.SheetNames = [];
    this.Sheets = {};
}
function s2ab(s) {
    var buf = new ArrayBuffer(s.length);
    var view = new Uint8Array(buf);
    for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
    return buf;
}
export function export_table_to_excel(id) {
    var theTable = document.getElementById(id);
    var oo = generateArray(theTable);
    var ranges = oo[1];
    /* original data */
    var data = oo[0];
    var ws_name = "SheetJS";
    var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
    /* add ranges to worksheet */
    // ws['!cols'] = ['apple', 'banan'];
    ws['!merges'] = ranges;
    /* add worksheet to workbook */
    wb.SheetNames.push(ws_name);
    wb.Sheets[ws_name] = ws;
    var wbout = XLSX.write(wb, { bookType: 'xlsx', bookSST: false, type: 'binary' });
    saveAs(new Blob([s2ab(wbout)], { type: "application/octet-stream" }), "test.xlsx")
}
function formatJson(jsonData) {
}
export function export_json_to_excel(th, jsonData, defaultTitle) {
    /* original data */
    var data = jsonData;
    data.unshift(th);
    let merges = [];
    let mergeItem = {}
    for (let i = 0; i < data.length; i++) {
      for (let j = 0; j < data[i].length; j++) {
        if (data[i][j] !== null || data[i - 1][j] === null) {
          continue
        }
        mergeItem.s = {
          r: i - 1,
          c: j
        }
        for (let k = i; k < data.length; k++) {
          if (data[k][j] === null && k !== data.length - 1) {
            continue
          }
          mergeItem.e = {
            r: data[k][j] !== null ? k - 1 : k,
            c: j
          }
          merges.push({...mergeItem})
          break
        }
      }
    }
    var ws_name = "SheetJS";
    var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
    ws['!merges'] = merges;
    /* add worksheet to workbook */
    wb.SheetNames.push(ws_name);
    wb.Sheets[ws_name] = ws;
    var wbout = XLSX.write(wb, { bookType: 'xlsx', bookSST: false, type: 'binary' });
    var title = defaultTitle || '列表'
    saveAs(new Blob([s2ab(wbout)], { type: "application/octet-stream" }), title + ".xlsx")
}


最后总结,在分页导出时自带调整分页大小,本次实战项目上整个10万数据导出基本上在1-2分钟左右,效果还是可以的。


点击QQ咨询
开通会员
上传资源赚钱
返回顶部
×
  • 微信支付
  • 支付宝付款
扫码支付
微信扫码支付
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载