likes
comments
collection
share

Excel导出,前后端两种方法让数据飞入你的手中!

作者站长头像
站长
· 阅读数 12

前言

今天我来教大家一个很有用的技能 —— 如何在前端实现导出 Excel。相信很多人都有过在 Excel 中频繁操作数据的痛苦经历,但是如果你能掌握这种技术,那么你就可以像 Excel 软件一样,轻松处理海量数据了!而且你还可以吊打那些不懂得如何在前端导出 Excel 的小伙伴们。

区别

在现在的实际工作中,导出 Excel 功能有2种常用的方法,一种就是前端根据数据自行使用Xlsx.js进行导出;还有一种就是调用后端接口获得二进制流然后再导出为 Excel。这篇文章就会给大家介绍这两种方式在前端的实现方法,下面会给出这2种方式的优劣势,然后掘友们可以根据实际的开发需求自行选择符合自身需求的方法进行使用。

前端实现导出 Excel 和后端使用二进制流实现导出 Excel 自的优劣势,下面是它们的比较:

前端实现导出Excel的优劣势:

优势:

  1. 减少后端服务器压力:前端实现导出 Excel 的过程中,数据处理和生成 Excel 文件都在前端完成,不需要向后端请求生成 Excel 文件,可以减少服务器的压力。
  2. 用户体验更好:前端导出 Excel 文件可以更快地响应用户的操作,用户可以直接下载和查看生成的 Excel 文件,可以提供更好的用户体验。

劣势:

  1. 浏览器兼容性问题:不同的浏览器对于 Excel 文件的生成和下载支持不同,可能会导致一些兼容性问题。
  2. 生成 Excel 文件的性能问题:如果要导出大量数据,前端生成 Excel 文件的性能可能会受到限制,导致用户等待时间过长或者浏览器崩溃。

后端使用二进制流实现导出Excel的优劣势:

优势:

  1. 生成 Excel 文件的性能更好:由于后端服务器通常有更好的计算资源,可以更快地生成大量数据的 Excel 文件。
  2. 可以实现更复杂的数据处理逻辑:后端实现导出 Excel 可以利用更强大的计算资源和更丰富的数据处理工具,实现更复杂的数据处理逻辑。

劣势:

  1. 增加后端服务器压力:后端实现导出 Excel 需要处理大量的数据,可能会增加服务器的负担。
  2. 用户体验相对较差:后端实现导出 Excel 需要等待服务器生成 Excel 文件,并通过下载链接或者 API 接口提供给用户,相对于前端实现导出 Excel,用户体验可能会差一些。

总的来说,前端实现导出 Excel 更适合数据量较小的情况,可以提供更好的用户体验,而后端使用二进制流实现导出 Excel 更适合处理大量数据和复杂的数据逻辑,但需要考虑服务器的负荷和用户体验。

实现方法

下面就开始来介绍上述2种方式的示例代码,抓紧扶稳了,马上开车了→

前端使用Xlsx.js实现导出Excel功能

导出纯文本数据可以使用Xlsx.js库来实现。Xlsx.js是一个强大的JavaScript库,可以帮助我们在前端中处理 Excel文件。

简单导出

import * as XLSX from 'xlsx';

// 获取数据
const data = [
  ['姓名', '年龄', '性别'],
  ['王二', 35, '男'],
  ['张三', 25, '男'],
  ['李四', 30, '女'],
  ['赵五', 40, '女'],
];

// 创建 Workbook 对象
const workbook = XLSX.utils.book_new();

// 创建 Worksheet 对象
const worksheet = XLSX.utils.json_to_sheet(data);

// 将 Worksheet 添加到 Workbook 中
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');

// 生成 Excel 文件并下载
XLSX.writeFile(workbook, 'data.xlsx');

首先,我们通过XLSX.utils.json_to_sheet()方法将数据转换为Worksheet对象,再通过 XLSX.utils.book_append_sheet()方法将Worksheet对象添加到Workbook对象中。最后,通过 XLSX.writeFile()方法将Workbook对象生成的Excel文件下载到客户端。

Excel导出,前后端两种方法让数据飞入你的手中!

导出多个sheet

上面的示例是简单的导出效果,最终导出的 Excel 只会有一个sheet,那如果需要导出的 Excel 有多个 Sheet 需要如何处理呢?那就接着往下看→

为了解决可以一次导出多个数据分别在多个 Sheet 的问题,我们来为上面的代码封装一下。如下:

import * as xlsx from 'xlsx';
import { JSON2SheetOpts, WorkBook } from 'xlsx';

/**
 * 导出 excel 文件
 * @param array JSON 数组
 * @param fileName 文件名
 */
interface SheetDataInfo {
  sheetName: string; // 表名
  data: any[]; // 表数据
  opts?: JSON2SheetOpts; // 表配置项
}
export function exportExcelFile(array: SheetDataInfo[], fileName: string) {
  const workBook: WorkBook = {
    SheetNames: [],
    Sheets: {},
  };
  array.forEach((item) => {
    const jsonWorkSheet = xlsx.utils.json_to_sheet(item.data, item.opts);
    workBook.SheetNames.push(item.sheetName);
    workBook.Sheets[item.sheetName] = jsonWorkSheet;
  });
  return xlsx.writeFile(workBook, fileName);
}

上述代码是一个使用Xlsx.js库实现前端导出 Excel 的函数,函数名为 exportExcelFile,参数为一个 SheetDataInfo 类型的数组和一个文件名。该函数会遍历传入的数组,生成对应的 Sheet 数据,并将其存入工作簿中,最后将工作簿写入指定文件名的 Excel 文件中,这样就能实现一个导出可以导出多个 Sheet 的功能了。

使用如下:

import { exportExcelFile } from '@/utils/common';

/**
 * 导出数据
 */
const handleExportData = () => {
  // 总览数据
  const info = {
    personNum: 90,
    timesNum: 7,
    total: 900
  }
  // 每日数据
  const data = [
    {name: '王二', age: 35, sex: '男'},
    {name: '张三', age: 25, sex: '男'},
    {name: '李四', age: 30, sex: '女'},
    {name: '赵五', age: 40, sex: '女'}
  ]
  const overViewData = {
    sheetName: '数据总览',
    data: [
      {
        人数: info.personNum,
        次数: info.timesNum,
        总数: info.total,
      },
    ],
  };

  const dailyData = {
    sheetName: '每日数据',
    data: data.map(item => {
      return {
        姓名: item.name,
        年龄: item.age,
        性别: item.sex,
      };
    }),
  };
  const exportData = [overViewData, dailyData];
  exportExcelFile(exportData, `数据统计.xlsx`);
}
    

Excel导出,前后端两种方法让数据飞入你的手中!

Excel导出,前后端两种方法让数据飞入你的手中!

可以看到,上述代码成功的导出了有2个 Sheet 页面的 Excel 文件,并且表格的每一列都有对应的标题。上述的代码获取数据的方式还可以通过调用后端接口进行获取后,对数据进行组装后进行导出。下面就来举个小例,如下:

import { exportExcelFile } from '@/utils/common';
import moment from 'moment';

/**
 * 导出数据
 */
const exportMatchListData = async () => {
  // 通过后端接口进行获取数据
  const result = await getDataList({
    id: id,
    pageIndex: 1, // 页索引
    pageSize: 9999, // 页项数
  });
  const list = result.data?.list || [];
  const data = {
    sheetName: '数据详情',
    data: (list || []).map((item: any) => {
        return {
          日期: moment(item.date).format('YYYY-MM-DD'),
          名称: item.name,
          品类: item.type,
          状态: item.status
        };
      })
  };
  const exportData = [data];
  // 使用moment为导出的文件加上时间
  exportExcelFile(exportData, `数据详情${moment().format('YYYYMMDDHHmmss')}.xlsx`);
};

以上就是前端自行通过Xlsx.js库实现前端导出 Excel 功能的主要代码,实现方法还是比较简单的,如果今后工作中后端不愿意写导出功能,就可以利用上面的方式去实现。

前端调用后端接口实现导出Excel功能

接着再来看看前端如果是调用后端接口来做导出功能又是如何实现的?具体操作如下:

  1. 创建一个新的 FileReader 实例。
  2. 通过 readAsDataURL() 方法将后端返回的二进制数据转换为 data URL,并将其存储在 FileReader 的 result 属性中。
  3. 当 FileReader 完成读取操作时,触发 onload 事件,将 data URL 赋值给创建的 a 标签元素的 href 属性。
  4. 设置 a 标签元素的 download 属性为传入的文件名,并对文件名进行 URI 解码。
  5. 将创建的 a 标签元素添加到页面中。
  6. 触发 a 标签元素的 click() 方法进行下载。
  7. 下载完成后,从页面中移除 a 标签元素。
  8. 返回从后端获取的响应数据。

那下面就用代码来实现,这里是将其封装为一个文件下载的通用方法,方便在实际项目中进行引用。如下:

import axios, {AxiosRequestConfig} from 'axios';

// 创建axios对象
const instance = axios.create();


// 请求配置接口
interface IRequestOptions extends AxiosRequestConfig {
    withToken?: boolean;
    isCad?: boolean;
}


/**
 * 对get参数进行URI编码并输出query字符串
 */
const encodeGetParams = (body: any) => {
    if (!body) {
        return '';
    }
    let newBody = {...body};
    let temp = [];
    for (let k in newBody) {
        temp.push(`${k}=${encodeURIComponent(newBody[k])}`);
    }
    return `${temp.join('&')}`;
}


/**
 * 文件下载接口
 * @param url
 * @param body
 * @param options
 */
export function fileDownload(url: string, body: any, options: IRequestOptions = {}) : Promise<any> {
    const { method = 'post', withToken = true } = options;
    return instance({
        url,
        data: (method.toLowerCase() == 'post' ? body : {}),
        params: (method.toLowerCase() == 'get' ? body : {}),
        method: 'post',
        baseURL: 'xxx', // 请求地址
        timeout: 200000,
        headers: {
            Authorization: 'Bearer ' + localStorage.getItem(USER_TOKEN) // token
        },
        responseType: 'blob',
        // 自定义参数序列化方法
        paramsSerializer: (params: any) => {
            return encodeGetParams(params);
        },
        ...options,
    }).then(res => {
        // 获取文件名 后端需要在响应头增加filename,这样导出的文件名也由后端进行控制
        const { filename } = res.headers;

        // 兼容IE和Edge
        if ("ActiveXObject" in window || navigator.userAgent.indexOf("Edge") > -1) {
            window.navigator.msSaveBlob(res.data, decodeURIComponent(filename));
            return res;
        }

        let reader = new FileReader()
        reader.readAsDataURL(res.data)
        reader.onload = (e: any) => {
            let a = document.createElement('a');
            a.download = decodeURIComponent(filename);
            a.href = e.target.result
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
        }
        return res;
    });
}

这段代码的作用是将从后端获取到的二进制数据转换为 data URL,并通过创建一个 a 标签元素,设置其 download 属性和 href 属性,最后触发其 click() 方法进行文件下载。

然后在项目中进行引用,如下:

/**
 * 下载模板
 */
const downloadFile = () => {
    let params = {
        id: id,
        date: '2023-03-10'
    }
    fileDownload('/excel/tempate/export', params, {
        method: 'get'
    })
}

这样就可以实现通过调用后端接口进行导出 Excel 的功能了,上述封装的文件下载接口不是完整的,还有拦截和处理错误码的逻辑没有加上,但是导出功能的代码按照上述代码是可行的。

最后,前端实现导出 Excel 文件可以使用第三方库Xlsx.js,通过将 JSON 数据转换为 Excel 的工作簿(WorkBook)对象,再通过 writeFile() 方法将工作簿对象保存为 Excel 文件。这种方式的优点是简单易用,可以在前端实现数据的导出,不需要请求后端接口。

后端使用二进制流实现导出 Excel 文件则需要在后端实现相应的接口,并将数据按照 Excel 文件格式进行编码,再返回给前端。这种方式的优点是可以在后端控制数据的生成和导出,同时也可以实现较为复杂的数据处理和导出功能。

两种方式各有优缺点,可以根据具体需求选择适合的实现方式。无论是前端还是后端,实现 Excel 文件导出的功能都是需要掌握的。所以,掘友们赶紧卷起来吧!

后语

小伙伴们,如果觉得本文对你有些许帮助,点个👍或者➕个关注再走呗^_^ 。另外如果本文章有问题或有不理解的部分,欢迎大家在评论区评论指出,我们一起讨论共勉。

Excel导出,前后端两种方法让数据飞入你的手中!