JS前端導(dǎo)出Excel的方法詳解
前言
最近寫(xiě)管理端的需求,發(fā)現(xiàn)有一個(gè)excel導(dǎo)出的需求,本來(lái)是后端同學(xué)負(fù)責(zé),但是因?yàn)樗麄兲α?,把這塊任務(wù)交給前端了,起初產(chǎn)品覺(jué)得前端實(shí)現(xiàn)不了,一聽(tīng)這話,這我哪里受得了,趕緊寫(xiě)了個(gè)demo給她看,前端是可以實(shí)現(xiàn)的。enen,產(chǎn)品看了直夸牛逼
接下來(lái),我來(lái)分享導(dǎo)出excel文件的三種實(shí)現(xiàn)方式
url下載
在這種方式中,我們的目標(biāo)是后端生成Excel文件并提供一個(gè)地址,前端通過(guò)訪問(wèn)這個(gè)地址來(lái)下載導(dǎo)出的Excel文件。
- 后端根據(jù)前端的請(qǐng)求,生成需要導(dǎo)出的數(shù)據(jù),并將數(shù)據(jù)轉(zhuǎn)換為Excel格式的文件。
- 后端將生成的Excel文件保存到服務(wù)器的某個(gè)臨時(shí)目錄,并為該文件生成一個(gè)臨時(shí)的訪問(wèn)地址。
- 后端將生成的臨時(shí)地址返回給前端作為響應(yīng)。
- 前端收到后端返回的地址后,可以通過(guò)創(chuàng)建一個(gè)隱藏的
<a>
標(biāo)簽,并設(shè)置其href
屬性為后端返回的地址,然后觸發(fā)點(diǎn)擊該標(biāo)簽的操作,從而實(shí)現(xiàn)文件下載。 - 前端完成下載后,可以根據(jù)需求決定是否刪除服務(wù)器上的臨時(shí)文件。
// 后端接口:/api/export/excel // 請(qǐng)求方式:GET // 假設(shè)后端接口返回導(dǎo)出地址的數(shù)據(jù)格式為 { url: "https://example.com/excel_exports/exported_file.xlsx" } export const exportExcelViaURL = () => { // 發(fā)起后端接口請(qǐng)求獲取導(dǎo)出地址 fetch('/api/export/excel') .then((response) => response.json()) .then((data) => { const { url } = data; // 創(chuàng)建一個(gè)隱藏的<a>標(biāo)簽并設(shè)置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`; // 觸發(fā)點(diǎn)擊操作,開(kāi)始下載文件 link.click(); }) .catch((error) => { console.error('導(dǎo)出Excel失敗:', error); }); };
Blob文件流
后端直接返回Blob文件流數(shù)據(jù),前端通過(guò)接收到的Blob數(shù)據(jù)進(jìn)行文件下載。
- 后端根據(jù)前端的請(qǐng)求,生成需要導(dǎo)出的數(shù)據(jù),并將數(shù)據(jù)轉(zhuǎn)換為Excel格式的文件。
- 后端將生成的Excel數(shù)據(jù)以Blob文件流的形式返回給前端,通常是通過(guò)設(shè)置響應(yīng)的Content-Type和Content-Disposition頭,使其以文件下載的方式呈現(xiàn)給用戶。
- 前端通過(guò)接收到的Blob數(shù)據(jù),可以創(chuàng)建一個(gè)Blob URL,然后創(chuàng)建一個(gè)隱藏的
<a>
標(biāo)簽,并將其href
屬性設(shè)置為Blob URL,再觸發(fā)點(diǎn)擊該標(biāo)簽的操作,從而實(shí)現(xiàn)文件下載。
// 后端接口:/api/export/excel/blob // 請(qǐng)求方式:GET export const exportExcelViaBlob = () => { // 發(fā)起后端接口請(qǐng)求獲取Blob文件流數(shù)據(jù) fetch('/api/export/excel/blob') .then((response) => response.blob()) .then((blobData) => { // 創(chuàng)建Blob URL const blobUrl = URL.createObjectURL(blobData); // 創(chuàng)建一個(gè)隱藏的<a>標(biāo)簽并設(shè)置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`; // 觸發(fā)點(diǎn)擊操作,開(kāi)始下載文件 link.click(); // 釋放Blob URL URL.revokeObjectURL(blobUrl); }) .catch((error) => { console.error('導(dǎo)出Excel失敗:', error); }); };
基于XLSX
XLSX是一款功能強(qiáng)大的JavaScript庫(kù),用于在瀏覽器和Node.js中讀取、解析、處理和寫(xiě)入Excel文件。
1. 安裝XLSX
首先,你需要在你的項(xiàng)目中安裝XLSX庫(kù)。你可以通過(guò)npm或yarn來(lái)安裝:
npm install xlsx
或者
yarn add xlsx
2. 引入XLSX
在你的代碼中,你需要引入XLSX庫(kù),以便使用其中的功能:
import * as XLSX from 'xlsx';
3. 讀取Excel文件
使用XLSX庫(kù),你可以讀取現(xiàn)有的Excel文件,提取其中的數(shù)據(jù)和元數(shù)據(jù)。例如,假設(shè)你有一個(gè)名為"data.xlsx"的Excel文件,你可以通過(guò)以下方式讀取它:
import * as XLSX from 'xlsx'; const file = 'data.xlsx'; // 文件路徑或URL const workbook = XLSX.readFile(file); const sheetName = workbook.SheetNames[0]; // 假設(shè)我們讀取第一個(gè)工作表 const worksheet = workbook.Sheets[sheetName]; const data = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); console.log(data);
4. 寫(xiě)入Excel文件
除了讀取現(xiàn)有的Excel文件,XLSX庫(kù)還允許你將數(shù)據(jù)寫(xiě)入到新的Excel文件中。例如,你可以將一個(gè)二維數(shù)組的數(shù)據(jù)寫(xiě)入到一個(gè)新的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'; // 導(dǎo)出的文件名 XLSX.writeFile(workbook, fileName);
項(xiàng)目實(shí)踐
下面代碼是我在項(xiàng)目中封裝好的代碼,有需要的同學(xué)直接copy使用就行
為了實(shí)現(xiàn)前述兩種方式的前端導(dǎo)出Excel功能,我們將使用XLSX庫(kù)來(lái)處理數(shù)據(jù)并導(dǎo)出Excel文件。
// 基于XLSX的前端導(dǎo)出Excel實(shí)現(xiàn) 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 定義列屬性數(shù)組 * @param data 數(shù)據(jù) * @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數(shù)據(jù) const excelData = XLSX.write(workbook, { type: 'array', bookType: 'xlsx' }); const blobData = new Blob([excelData], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); // 創(chuàng)建Blob URL const blobUrl = URL.createObjectURL(blobData); // 創(chuàng)建一個(gè)隱藏的<a>標(biāo)簽并設(shè)置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`; // 觸發(fā)點(diǎn)擊操作,開(kāi)始下載文件 link.click(); // 釋放Blob URL URL.revokeObjectURL(blobUrl); };
下載全部
我們可能需要一鍵下載所有表格的數(shù)據(jù),這時(shí)候前端需要輪詢后端的接口,拿到所有的數(shù)據(jù),所以我們需要實(shí)現(xiàn)一個(gè)loopReuqest函數(shù)
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; } /** * 循環(huán)分頁(yè)請(qǐng)求,獲取全部數(shù)據(jù) * @param {Function} request 請(qǐng)求 * @param {Number} size 頁(yè)大小 * @param {Object} params 其余參數(shù) * @param {String} listLabel.pageLable 當(dāng)前頁(yè)字段名。默認(rèn)page * @param {String} listLabel.sizeLabel 頁(yè)大小字段名。默認(rèn)page_size * @param {String} listLabel.totalLabel 總條數(shù)字段名。默認(rèn)total * @param {String} listLabel.itemsLabel 數(shù)據(jù)列表字段名。默認(rèn)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 []; }
以上就是JS前端導(dǎo)出Excel的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于JS導(dǎo)出Excel的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信小程序?qū)崙?zhàn)篇之購(gòu)物車的實(shí)現(xiàn)代碼示例
本篇文章主要介紹了微信小程序?qū)崙?zhàn)篇之購(gòu)物車的實(shí)現(xiàn)代碼示例,詳細(xì)的介紹了購(gòu)物車的功能實(shí)現(xiàn),具有一定的參考價(jià)值,有興趣的可以了解一下2017-11-11從階乘函數(shù)對(duì)比Javascript和C#的異同
今天學(xué)習(xí)Javascript函數(shù),發(fā)現(xiàn)這完全是一個(gè)神奇的東西。跟我們平常所見(jiàn)強(qiáng)類型語(yǔ)言中的函數(shù)有好多不同。下面我們就從C#和JavaScript的兩個(gè)計(jì)算階乘的函數(shù)中比較兩者的異同2012-05-05js 獲取元素的具體樣式信息getcss(實(shí)例講解)
下面小編就為大家?guī)?lái)一篇js 獲取元素的具體樣式信息getcss(實(shí)例講解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07JavaScript實(shí)現(xiàn)簡(jiǎn)易計(jì)算器功能的兩種方法
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)簡(jiǎn)易計(jì)算器功能的兩種方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07