Front-end export Excel practice: exploring the implementation of xlsl

Table of contents

foreword

url download

Blob file stream

Based on XLSX

1. Install XLSX

2. Import XLSX

3. Read Excel file

4. Write Excel file

Project Practice

download all

share


foreword

Recently, I wrote about the requirements of the management end, and found that there was an excel export requirement. Originally, the back-end students were responsible for it, but because they were too busy, they handed over this task to the front-end. enen, the product is so awesome

Next, let me share three implementations of exporting excel files

url download

In this way, our goal is to generate an Excel file at the back end and provide an address, and the front end can download the exported Excel file by accessing this address.

  • According to the request of the front end, the back end generates the data that needs to be exported, and converts the data into a file in Excel format.

  • The backend saves the generated Excel file to a temporary directory of the server, and generates a temporary access address for the file.

  • The backend returns the generated temporary address to the frontend as a response.

  • After the front-end receives the address returned by the back-end, it can create a hidden  <a> label, set its  href attribute as the address returned by the back-end, and then trigger the operation of clicking the label to realize file download.

  • After the front-end completes the download, you can decide whether to delete the temporary files on the server according to your needs.

 
 
// 后端接口:/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 file stream

The backend directly returns the Blob file stream data, and the frontend downloads the file through the received Blob data.

  • According to the request of the front end, the back end generates the data that needs to be exported, and converts the data into a file in Excel format.

  • The backend returns the generated Excel data to the frontend in the form of a Blob file stream, usually by setting the Content-Type and Content-Disposition headers of the response, so that it can be presented to the user as a file download.

  • The front end can create a Blob URL through the received Blob data, then create a hidden  <a> label,  href set its attribute to the Blob URL, and then trigger the operation of clicking on the label to realize file download.

 
 
// 后端接口:/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);
    });
};

Based on XLSX

XLSX is a powerful JavaScript library for reading, parsing, manipulating and writing Excel files in the browser and Node.js.

1. Install XLSX

First, you need to install the XLSX library in your project. You can install via npm or yarn:

 
 
npm install xlsx

or

 
 
yarn add xlsx

2. Import XLSX

In your code, you need to import the XLSX library in order to use its functions:

 
 
import * as XLSX from 'xlsx';

3. Read Excel file

Using the XLSX library, you can read existing Excel files and extract data and metadata from them. For example, suppose you have an Excel file named "data.xlsx", you can read it with:

 
 
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. Write Excel file

In addition to reading existing Excel files, the XLSX library also allows you to write data into new Excel files. For example, you can write the data of a 2D array to a new Excel file:

 
 
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);

Project Practice

The following code is the code I encapsulated in the project, students who need it can just copy and use it

In order to realize the front-end export Excel function of the above two methods, we will use the XLSX library to process the data and export the Excel file.

 
 
// 基于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);
};

download all

We may need to download all table data with one click. At this time, the front-end needs to poll the back-end interface to get all the data, so we need to implement a loopReuqest function

 
 
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 [];
}

share

Finally, I would like to recommend a platform for learning the front end, and you can watch all the videos after opening the black gold card! It only costs 39 yuan.

There is contact information below

 

 

Guess you like

Origin blog.csdn.net/m0_68036862/article/details/131917648