📃前端导出Excel实践:探索xlsl的实现方式

简介: 前言最近写管理端的需求,发现有一个excel导出的需求,本来是后端同学负责,但是因为他们太忙了,把这块任务交给前端了,起初产品觉得前端实现不了,一听这话,这我哪里受得了,赶紧写了个demo给她看,前端是可以实现的。enen,产品看了直夸牛逼接下来,我来分享导出excel文件的三种实现方式url下载在这种方式中,我们的目标是后端生成Excel文件并提供一个地址,前端通过访问这个地址来下载导出的Excel文件。后端根据前端的请求,生成需要导出的数据,并将数据转换为Excel格式的文件。后端将生成的Excel文件保存到服务器的某个临时目录,并为该文件生成一个临时的访问地址

前言

最近写管理端的需求,发现有一个excel导出的需求,本来是后端同学负责,但是因为他们太忙了,把这块任务交给前端了,起初产品觉得前端实现不了,一听这话,这我哪里受得了,赶紧写了个demo给她看,前端是可以实现的。enen,产品看了直夸牛逼

接下来,我来分享导出excel文件的三种实现方式

url下载

在这种方式中,我们的目标是后端生成Excel文件并提供一个地址,前端通过访问这个地址来下载导出的Excel文件。

  • 后端根据前端的请求,生成需要导出的数据,并将数据转换为Excel格式的文件。
  • 后端将生成的Excel文件保存到服务器的某个临时目录,并为该文件生成一个临时的访问地址。
  • 后端将生成的临时地址返回给前端作为响应。
  • 前端收到后端返回的地址后,可以通过创建一个隐藏的 <a> 标签,并设置其 href 属性为后端返回的地址,然后触发点击该标签的操作,从而实现文件下载。
  • 前端完成下载后,可以根据需求决定是否删除服务器上的临时文件。
// 后端接口:/api/export/excel
// 请求方式:GET
// 假设后端接口返回导出地址的数据格式为 { url: "https://example.com/excel_exports/exported_file.xlsx" }
export const exportExcelViaURL = () => {
  // 发起后端接口请求获取导出地址
  fetch('/api/export/excel')
    .then((response) => response.json())
    .then((data) => {
      const { url } = data;
      // 创建一个隐藏的<a>标签并设置href属性为后端返回的地址
      const link = document.createElement('a');
      link.href = url;
      link.target = '_blank';
      link.download = `exported_data_${dayjs().format('YYYY-MM-DD_hh.mm.ss_a')}.xlsx`;
      // 触发点击操作,开始下载文件
      link.click();
    })
    .catch((error) => {
      console.error('导出Excel失败:', error);
    });
};

Blob文件流

后端直接返回Blob文件流数据,前端通过接收到的Blob数据进行文件下载。

  • 后端根据前端的请求,生成需要导出的数据,并将数据转换为Excel格式的文件。
  • 后端将生成的Excel数据以Blob文件流的形式返回给前端,通常是通过设置响应的Content-Type和Content-Disposition头,使其以文件下载的方式呈现给用户。
  • 前端通过接收到的Blob数据,可以创建一个Blob URL,然后创建一个隐藏的 <a> 标签,并将其 href 属性设置为Blob URL,再触发点击该标签的操作,从而实现文件下载。
// 后端接口:/api/export/excel/blob
// 请求方式:GET
export const exportExcelViaBlob = () => {
  // 发起后端接口请求获取Blob文件流数据
  fetch('/api/export/excel/blob')
    .then((response) => response.blob())
    .then((blobData) => {
      // 创建Blob URL
      const blobUrl = URL.createObjectURL(blobData);
      // 创建一个隐藏的<a>标签并设置href属性为Blob URL
      const link = document.createElement('a');
      link.href = blobUrl;
      link.target = '_blank';
      link.download = `exported_data_${dayjs().format('YYYY-MM-DD_hh.mm.ss_a')}.xlsx`;
      // 触发点击操作,开始下载文件
      link.click();
      // 释放Blob URL
      URL.revokeObjectURL(blobUrl);
    })
    .catch((error) => {
      console.error('导出Excel失败:', error);
    });
};


基于XLSX

XLSX是一款功能强大的JavaScript库,用于在浏览器和Node.js中读取、解析、处理和写入Excel文件。

1. 安装XLSX

首先,你需要在你的项目中安装XLSX库。你可以通过npm或yarn来安装:

基于XLSX
XLSX是一款功能强大的JavaScript库,用于在浏览器和Node.js中读取、解析、处理和写入Excel文件。
1. 安装XLSX
首先,你需要在你的项目中安装XLSX库。你可以通过npm或yarn来安装:

或者

yarn add xlsx

2. 引入XLSX

在你的代码中,你需要引入XLSX库,以便使用其中的功能:

import * as XLSX from 'xlsx';

3. 读取Excel文件

使用XLSX库,你可以读取现有的Excel文件,提取其中的数据和元数据。例如,假设你有一个名为"data.xlsx"的Excel文件,你可以通过以下方式读取它:

import * as XLSX from 'xlsx';
const file = 'data.xlsx'; // 文件路径或URL
const workbook = XLSX.readFile(file);
const sheetName = workbook.SheetNames[0]; // 假设我们读取第一个工作表
const worksheet = workbook.Sheets[sheetName];
const data = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
console.log(data);

4. 写入Excel文件

除了读取现有的Excel文件,XLSX库还允许你将数据写入到新的Excel文件中。例如,你可以将一个二维数组的数据写入到一个新的Excel文件:

import * as XLSX from 'xlsx';
const data = [
  ['Name', 'Age', 'City'],
  ['John Doe', 30, 'New York'],
  ['Jane Smith', 25, 'San Francisco'],
];
const worksheet = XLSX.utils.aoa_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
const fileName = 'output.xlsx'; // 导出的文件名
XLSX.writeFile(workbook, fileName);

项目实践

下面代码是我在项目中封装好的代码,有需要的同学直接copy使用就行

为了实现前述两种方式的前端导出Excel功能,我们将使用XLSX库来处理数据并导出Excel文件。

// 基于XLSX的前端导出Excel实现
import dayjs from 'dayjs';
import * as XLSX from 'xlsx';
/**
 * eg: .columns = [
 *    { header: 'Id', key: 'id', wpx: 10 },
 *    { header: 'Name', key: 'name', wch: 32 },
 *    { header: 'D.O.B.', key: 'dob', width: 10, hidden: true }
 * ]
 * data: [{id: 1, name: 'John Doe', dob: new Date(1970,1,1)}]
 * @param columns 定义列属性数组
 * @param data  数据
 * @param name  文件名
 */
export const generateExcel = (columns = [], data = [], name = '') => {
  const headers = columns.map((item) => item.header);
  // https://docs.sheetjs.com/docs/csf/features/#row-and-column-properties
  const otherConfigs = columns.map(({ key, header, ...item }) => item);
  const dataList = data.map((item) => {
    let obj = {};
    columns.forEach((col) => {
      obj[col.header] = item[col.key];
    });
    return obj;
  });
  const workbook = XLSX.utils.book_new();
  workbook.SheetNames.push(name);
  const worksheet = XLSX.utils.json_to_sheet(dataList, {
    header: headers,
  });
  worksheet['!cols'] = otherConfigs;
  workbook.Sheets[name] = worksheet;
  // 生成Blob数据
  const excelData = XLSX.write(workbook, { type: 'array', bookType: 'xlsx' });
  const blobData = new Blob([excelData], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
  // 创建Blob URL
  const blobUrl = URL.createObjectURL(blobData);
  // 创建一个隐藏的<a>标签并设置href属性为Blob URL
  const link = document.createElement('a');
  link.href = blobUrl;
  link.target = '_blank';
  link.download = `${name}-${dayjs().format('YYYY-MM-DD_hh.mm.ss_a')}.xlsx`;
  // 触发点击操作,开始下载文件
  link.click();
  // 释放Blob URL
  URL.revokeObjectURL(blobUrl);
};

下载全部

我们可能需要一键下载所有表格的数据,这时候前端需要轮询后端的接口,拿到所有的数据,所以我们需要实现一个loopReuqest函数

export default function awaitRequest(limit = 5) {
  let awaitTask = [];
  let currentTaskNum = 0;
  function run(event, ...args) {
    return new Promise((resolve, reject) => {
      function callbackEvent() {
        currentTaskNum++;
        event(...args)
          .then((res) => {
            if (awaitTask.length) {
              const nextTask = awaitTask.shift();
              nextTask();
            }
            resolve(res);
          })
          .catch((e) => {
            console.error(e);
            reject(e);
          })
          .finally(() => {
            currentTaskNum--;
          });
      }
      if (currentTaskNum >= limit) {
        awaitTask.push(callbackEvent);
      } else {
        callbackEvent();
      }
    });
  }
  Object.defineProperties(run, {
    clear: {
      value: () => {
        awaitTask = [];
      },
    },
  });
  return run;
}
/**
 * 循环分页请求,获取全部数据
 * @param {Function} request 请求
 * @param {Number} size 页大小
 * @param {Object} params 其余参数
 * @param {String} listLabel.pageLable 当前页字段名。默认page
 * @param {String} listLabel.sizeLabel 页大小字段名。默认page_size
 * @param {String} listLabel.totalLabel 总条数字段名。默认total
 * @param {String} listLabel.itemsLabel 数据列表字段名。默认list
 * @returns
 */
export async function loopRequest(
  request,
  size,
  params,
  listLabel = {
    totalLabel: 'total',
    pageLable: 'page',
    sizeLabel: 'page_size',
    itemsLabel: 'list',
  },
) {
  const {
    totalLabel = 'total',
    pageLable = 'page',
    sizeLabel = 'page_size',
    itemsLabel = 'list'
  } = listLabel;
  try {
    const firstRes = await request({
      ...params,
      [sizeLabel]: size,
      [pageLable]: 1,
    });
    let list = firstRes.data[itemsLabel] || [];
    const total = firstRes.data[totalLabel];
    if (total > size) {
      const limit = awaitRequest();
      const restRequest = Array.from({
        length: Math.floor(total / size),
      }).map((_, index) =>
        limit(() =>
          request({
            ...params,
            [sizeLabel]: size,
            [pageLable]: index + 2,
          }),
        ),
      );
      const resetRes = await Promise.all(restRequest);
      resetRes.forEach((res) => {
        if (res.code === 0 && res.data[itemsLabel]) {
          list.push(...res.data[itemsLabel]);
        }
      });
    }
    return list;
  } catch (e) {
    console.error(e);
  }
  return [];
}

目录
相关文章
|
5月前
|
Python
Excel中如何批量重命名工作表与将每个工作表导出到单独Excel文件
本文介绍了如何在Excel中使用VBA批量重命名工作表、根据单元格内容修改颜色,以及将工作表导出为独立文件的方法。同时提供了Python实现导出工作表的代码示例,适用于自动化处理Excel文档。
|
7月前
|
存储 消息中间件 前端开发
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
校园圈子系统校园论坛小程序采用uni-app前端框架,支持多端运行,结合PHP后端(如ThinkPHP/Laravel),实现用户认证、社交关系管理、动态发布与实时聊天功能。前端通过组件化开发和uni.request与后端交互,后端提供RESTful API处理业务逻辑并存储数据于MySQL。同时引入Redis缓存热点数据,RabbitMQ处理异步任务,优化系统性能。核心功能包括JWT身份验证、好友系统、WebSocket实时聊天及活动管理,确保高效稳定的用户体验。
433 4
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
|
缓存 前端开发 JavaScript
利用代码分割优化前端性能:策略与实践
在现代Web开发中,代码分割是提升页面加载性能的有效手段。本文介绍代码分割的概念、重要性及其实现策略,包括动态导入、路由分割等方法,并探讨在React、Vue、Angular等前端框架中的具体应用。
|
6月前
|
Java 测试技术 数据库
spring号码归属地批量查询,批量查询号码归属地,在线工具,可按省份城市运营商号段分类分开分别导出excel表格
简介:文章探讨Spring Boot项目启动优化策略,通过自定义监听器、异步初始化及分库分表加载优化等手段,将项目启动时间从280秒缩短至159秒,提升约50%,显著提高开发效率。
|
10月前
|
JSON 前端开发 API
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
510 5
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
|
12月前
|
编解码 前端开发 开发者
探索无界:前端开发中的响应式设计深度实践与思考###
本文将带你领略响应式设计的精髓,一种超越传统页面布局限制的设计策略,它要求开发者以灵活多变的思维,打造能够无缝适应各种设备与屏幕尺寸的Web体验。通过深入浅出的讲解、实际案例分析以及技术实现细节的探讨,本文目的是激发读者对于响应式设计深层次的理解与兴趣,鼓励在实际应用中不断创新与优化。 ###
382 10
|
编解码 前端开发 开发者
前端开发中的响应式设计实践
前端开发中的响应式设计实践
|
数据格式 UED
记录一次NPOI库导出Excel遇到的小问题解决方案
【11月更文挑战第16天】本文记录了使用 NPOI 库导出 Excel 过程中遇到的三个主要问题及其解决方案:单元格数据格式错误、日期格式不正确以及合并单元格边框缺失。通过自定义单元格样式、设置数据格式和手动添加边框,有效解决了这些问题,提升了导出文件的质量和用户体验。
947 3
|
前端开发
实现Excel文件和其他文件导出为压缩包,并导入
实现Excel文件和其他文件导出为压缩包,并导入
252 1
|
编解码 前端开发 UED
探索无界:前端开发中的响应式设计哲学与实践####
本文不拘泥于传统摘要的框架,而是以一种对话的方式,引领读者踏入响应式设计的奇妙世界。想象一下,互联网如同一片浩瀚的海洋,而网页则是航行其中的船只。在这片不断变化的海域中,如何让我们的“船只”既稳固又灵活地适应各种屏幕尺寸和设备?这正是响应式设计的魅力所在。通过深入浅出的探讨,我们将一同揭开它背后的哲学思想与实战技巧,让你的网页在任何设备上都能展现出最佳姿态。 ####
127 0

热门文章

最新文章

  • 1
    Python 高效清理 Excel 空白行列:从原理到实战
  • 2
    前端如何存储数据:Cookie、LocalStorage 与 SessionStorage 全面解析
  • 3
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(五):背景属性;float浮动和position定位;详细分析相对、绝对、固定三种定位方式;使用浮动并清除浮动副作用
  • 4
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(六):全方面分析css的Flex布局,从纵、横两个坐标开始进行居中、两端等元素分布模式;刨析元素间隔、排序模式等
  • 5
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(一):CSS发展史;CSS样式表的引入;CSS选择器使用,附带案例介绍
  • 6
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(八):学习transition过渡属性;本文学习property模拟、duration过渡时间指定、delay时间延迟 等多个参数
  • 7
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(九):强势分析Animation动画各类参数;从播放时间、播放方式、播放次数、播放方向、播放状态等多个方面,完全了解CSS3 Animation
  • 8
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(二):CSS伪类:UI伪类、结构化伪类;通过伪类获得子元素的第n个元素;创建一个伪元素展示在页面中;获得最后一个元素;处理聚焦元素的样式
  • 9
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(四):元素盒子模型;详细分析边框属性、盒子外边距
  • 10
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(七):学习ransform属性;本文学习 rotate旋转、scale缩放、skew扭曲、tanslate移动、matrix矩阵 多个参数
  • 1
    前端如何存储数据:Cookie、LocalStorage 与 SessionStorage 全面解析
    510
  • 2
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(九):强势分析Animation动画各类参数;从播放时间、播放方式、播放次数、播放方向、播放状态等多个方面,完全了解CSS3 Animation
    191
  • 3
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(八):学习transition过渡属性;本文学习property模拟、duration过渡时间指定、delay时间延迟 等多个参数
    189
  • 4
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(七):学习ransform属性;本文学习 rotate旋转、scale缩放、skew扭曲、tanslate移动、matrix矩阵 多个参数
    145
  • 5
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(六):全方面分析css的Flex布局,从纵、横两个坐标开始进行居中、两端等元素分布模式;刨析元素间隔、排序模式等
    251
  • 6
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(五):背景属性;float浮动和position定位;详细分析相对、绝对、固定三种定位方式;使用浮动并清除浮动副作用
    359
  • 7
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(四):元素盒子模型;详细分析边框属性、盒子外边距
    157
  • 8
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(三):元素继承关系、层叠样式规则、字体属性、文本属性;针对字体和文本作样式修改
    95
  • 9
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(二):CSS伪类:UI伪类、结构化伪类;通过伪类获得子元素的第n个元素;创建一个伪元素展示在页面中;获得最后一个元素;处理聚焦元素的样式
    160
  • 10
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(一):CSS发展史;CSS样式表的引入;CSS选择器使用,附带案例介绍
    225