前端解析包含圖片的excel文件完整步驟及代碼示例
一、前言
1、問題描述
有時候難免會遇到解析excel的情況,現(xiàn)在前端的很多插件都可以實現(xiàn)excel文件中文本內(nèi)容的解析;但是很多時候excel文件中是帶有圖片文件的,這個圖片文件的提取著實是讓人有點頭疼的;
本人查閱了很多資料,試了很多方法,結(jié)果都是以失敗告終!
現(xiàn)決定使用一個迂回戰(zhàn)術(shù),完成一次曲線救國,哈哈哈,方法可能不太好,但勉強能夠使用,如果有哪位大佬看見,還望指點迷津,跪謝~
2、excel文件
3、實現(xiàn)效果
二、實現(xiàn)思路
- 第一步,使用XLSX插件,解析excel中的文本內(nèi)容;
- 第二步,使用JSZip插件,解析excel中的圖片內(nèi)容;
- 第三步,將圖片數(shù)據(jù)和文本數(shù)據(jù)進行整理,封裝成我們最終需要的數(shù)據(jù)格式;
三、完整代碼
<template> <div class="container"> <!-- 長傳組件 --> <el-upload action="" :before-upload="beforeUpload" :http-request="() => {}"> <el-button type="primary">導(dǎo)入excel</el-button> </el-upload> <!-- 表格組件 --> <el-table :data="tableData" border style="width: auto; margin-top: 10px"> <el-table-column :prop="item" :label="item" align="center" v-for="(item, index) in tableColumnLabel" :key="index" > <template #default="scope" v-if="item == 'avatar'"> <img :src="scope.row.avatar" alt="" style="width: 200px" /> </template> </el-table-column> </el-table> </div> </template> <script setup> import { ref } from "vue"; import JSZip from "jszip"; // 引入jszip import * as XLSX from "xlsx"; // 引入xlsx const tableColumnLabel = ref([]); // 獲取表頭內(nèi)容 const tableData = ref([]); // 表格數(shù)據(jù) const imageList = ref([]); // 表格圖片 // 加載按鈕的回調(diào) async function beforeUpload(file) { // 解析圖片 imageList.value = await getExcelImage(file); // 解析數(shù)據(jù) getExcelData(file); } // 解析數(shù)據(jù) function getExcelData(file) { let fileReader = new FileReader(); // 構(gòu)建fileReader對象 fileReader.readAsArrayBuffer(file); // 讀取指定文件內(nèi)容 // 讀取操作完成時 fileReader.onload = function (e) { try { let data = e.target.result; // 取得數(shù)據(jù)data // console.log(data); let workbook = XLSX.read(data, { type: "binary" }); // 將data轉(zhuǎn)換成excel工作表數(shù)據(jù) // console.log("Excel工作簿", workbook); const worksheet = workbook.Sheets[workbook.SheetNames[0]]; // 獲取第一個工作表 // console.log("第一張工作表", worksheet); /* * XLSX.utils.sheet_to_json 輸出JSON格式數(shù)據(jù) * 獲取指定工作表中的數(shù)據(jù)sheetlist[],整個表中的數(shù)據(jù)存放在一個數(shù)組sheetlist中; * sheetlist數(shù)組中的每個元素均為一個數(shù)組rowlist,是每一行的數(shù)據(jù); */ const sheetlist = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); //console.log('sheetlist', sheetlist); // 封裝數(shù)據(jù) formatDate(sheetlist); } catch (e) { console.log("文件類型不正確"); return; } }; } // 封裝數(shù)據(jù) function formatDate(sheetlist) { try { if (sheetlist.length < 1) return; tableColumnLabel.value = sheetlist[0]; // 獲取表格列名 for (let i = 0; i < sheetlist.length - 1; i++) { // 這里length-1是因為我們的sheetlist[0]為列名,實際的數(shù)據(jù)要少 1; let obj = {}; for (let j = 0; j < sheetlist[0].length; j++) { // 頭像列,則取解析到的imageList中的相應(yīng)值; // 這里要求我們知道excel中存放圖片的列名,否則會導(dǎo)致讀取失??; if (sheetlist[0][j] == "avatar") { obj[sheetlist[0][j]] = `data:image/png;base64,${imageList.value[i]}`; // 注意base64編碼的處理 } else { // 非頭像列,直接取值; obj[sheetlist[0][j]] = sheetlist[i + 1][j] ?? ""; } } // console.log(obj); tableData.value.push(obj); // 添加到el-table綁定的數(shù)據(jù)源中 } console.log("tableData.value", tableData.value); } catch (error) { console.log(error); return; } } // 獲取圖片 async function getExcelImage(file) { // console.log(file); let imageList = []; // 用來存放圖片 const zip = new JSZip(); // 創(chuàng)建jszip實例 try { let zipLoadResult = await zip.loadAsync(file); // 將xlsx文件轉(zhuǎn)zip文件 // console.log("zipLoadResult", zipLoadResult); for (const key in zipLoadResult["files"]) { // 遍歷結(jié)果中的files對象 if (key.indexOf("media/image") != -1 && !key.dir) { await zip .file(zipLoadResult["files"][key].name) .async("base64") .then((res) => { imageList.push(res); // 將解析出的圖片的base64編碼值 先存入imageList數(shù)組中; }); } } } catch (error) { console.log(error); } // console.log('imageList', imageList); return imageList; } </script> <style lang="scss" scoped> </style>
四、準備工作
1、excel文件
先準備一個test.xlsx文件,內(nèi)容如下圖所示,主要帶上圖片就行;
2、上傳組件
添加一個上傳組件,用來上傳excel文件;這里采用的是element-plus中的el-upload組件;
<el-upload action="" :before-upload="beforeUpload" :http-request="() => {}"> <el-button type="primary">導(dǎo)入excel</el-button> </el-upload>
這個上傳組件并不是用來做上傳,只是利用它來讀取我們的excel文件;
后續(xù)我們將在beforeUpload()方法中處理導(dǎo)入的excel文件;
3、展示組件
添加一個數(shù)據(jù)展示組件,用來展示我們解析好的數(shù)據(jù);這里采用的是el-table組件;
注意這里的列是動態(tài)生成的,如果是圖片的列,則添加img標簽顯示圖片;
<el-table :data="tableData" border style="width: auto; margin-top: 10px"> <el-table-column :prop="item" :label="item" align="center" v-for="(item, index) in tableColumnLabel" :key="index"> <template #default="scope" v-if="item == 'avatar'"> <img :src="scope.row.avatar" alt="" style="width: 200px" /> </template> </el-table-column> </el-table>
表格需要用到的數(shù)據(jù);
import { ref } from "vue"; const tableColumnLabel = ref([]); // 獲取表頭內(nèi)容 const tableData = ref([]); // 表格數(shù)據(jù) const imageList = ref([]); // 表格圖片
4、安裝依賴
安裝jszip依賴;
npm install jszip 或 yarn add jszip
安裝xlsx依賴;
npm install xlsx 或 yarn xlsx
5、引入插件
在頁面中引入需要用的jszip和xlsx兩個依賴,可以使用其中的一些方法解析excel文件;
import JSZip from "jszip"; import * as XLSX from "xlsx";
6、現(xiàn)有效果
到此,準備工作完成,現(xiàn)有代碼與頁面效果如下所示:
<template> <div class="container"> <!-- 長傳組件 --> <el-upload action="" :before-upload="beforeUpload" :http-request="() => {}"> <el-button type="primary">導(dǎo)入excel</el-button> </el-upload> <!-- 表格組件 --> <el-table :data="tableData" border style="width: auto; margin-top: 10px"> <el-table-column :prop="item" :label="item" align="center" v-for="(item, index) in tableColumnLabel" :key="index"> <template #default="scope" v-if="item == 'avatar'"> <img :src="scope.row.avatar" alt="" style="width: 200px" /> </template> </el-table-column> </el-table> </div> </template> <script setup> import { ref } from "vue"; import JSZip from "jszip"; // 引入jszip import * as XLSX from "xlsx"; // 引入xlsx const tableColumnLabel = ref([]); // 獲取表頭內(nèi)容 const tableData = ref([]); // 表格數(shù)據(jù) const imageList = ref([]); // 表格圖片 // 加載按鈕的回調(diào) function beforeUpload(file){ console.log(file); } </script> <style lang="scss" scoped> </style>
可以在頁面看到一個導(dǎo)入按鈕和一個沒有任何數(shù)據(jù)的表格;
五、實現(xiàn)過程
1、加載excel
點擊導(dǎo)入excel按鈕,選擇我們之前準備好的測試excel【test.xlsx】文件,
導(dǎo)入按鈕的回調(diào):
可以在控制臺查看打印輸出的結(jié)果:
到這里呢,表示我們的excel已經(jīng)加載成功了,接下來繼續(xù)解析讀取到的內(nèi)容即可;
2、解析圖片文件
(1)解析思路
- 使用jszip將xlsx文件轉(zhuǎn)成zip文件;
- 獲取zip文件中的圖片內(nèi)容;
- 將圖片信息轉(zhuǎn)base64編碼存儲;
解析圖片文件我們需要用到j(luò)szip這個插件,至于為什么呢?因為我自己試了其他的方法都沒有成功;
首先,我們先將【test.xlsx】轉(zhuǎn)成【test.zip】文件;這里復(fù)制一份文件后 直接修改文件擴展名;壓縮是看不到效果的;
點開這個【test.zip】文件,可以看到以下內(nèi)容,
打開這個【xl】文件夾,在【media】下存放的是圖片文件,worksheets下存放的是文本文件;
所以,我們想要獲取excel中的圖片文件,只需訪問這個zip文件,然后獲取其中【media】文件夾中的內(nèi)容即可;
這個zip文件只是簡單查看,有助于我們理解的,并沒有解析的實際作用,后續(xù)刪除即可;
(2)解析過程
在beforeUpload()回調(diào)函數(shù)中,編寫解析代碼;
// 加載按鈕的回調(diào) async function beforeUpload(file){ // console.log(file); let imageList = []; // 用來存放圖片 const zip = new JSZip(); // 創(chuàng)建jszip實例 try { let zipLoadResult = await zip.loadAsync(file); // 將xlsx文件轉(zhuǎn)zip文件 console.log("zipLoadResult", zipLoadResult); // ...... } catch (error) { console.log(error); } return imageList; }
在打印輸出的結(jié)果中,我們可以看到跟之前自行手動轉(zhuǎn)zip后的結(jié)果是一致的;圖片文件在【xl/media】 路徑文件夾中;
繼續(xù)解析獲取到的files對象,
// 加載按鈕的回調(diào) async function beforeUpload(file){ // console.log(file); let imageList = []; // 用來存放圖片 const zip = new JSZip(); // 創(chuàng)建jszip實例 try { let zipLoadResult = await zip.loadAsync(file); // 將xlsx文件轉(zhuǎn)zip文件 // console.log("zipLoadResult", zipLoadResult); for (const key in zipLoadResult["files"]) { // 遍歷結(jié)果中的files對象 if (key.indexOf("media/image") != -1 && !key.dir) { await zip .file(zipLoadResult["files"][key].name) .async("base64") .then((res) => { imageList.push(res); // 將解析出的圖片的base64編碼值 先存入imageList數(shù)組中; }); } } } catch (error) { console.log(error); } console.log('imageList', imageList); return imageList; }
可以看到已經(jīng)成功獲取到了兩個圖片的base64編碼值;
在下方鏈接中,可以檢測取到的bae64是否正確:
到此,我們已經(jīng)取到了excel文件中的圖片信息imageList?。?/strong>
最后,我們將這段解析excel中圖片的代碼進行封裝,后續(xù)在其他地方也可以使用;
// 加載按鈕的回調(diào) async function beforeUpload(file){ // 解析圖片 imageList.value = await getExcelImage(file); } // 獲取圖片 async function getExcelImage(file){ // console.log(file); let imageList = []; // 用來存放圖片 const zip = new JSZip(); // 創(chuàng)建jszip實例 try { let zipLoadResult = await zip.loadAsync(file); // 將xlsx文件轉(zhuǎn)zip文件 // console.log("zipLoadResult", zipLoadResult); for (const key in zipLoadResult["files"]) { // 遍歷結(jié)果中的files對象 if (key.indexOf("media/image") != -1 && !key.dir) { await zip .file(zipLoadResult["files"][key].name) .async("base64") .then((res) => { imageList.push(res); // 將解析出的圖片的base64編碼值 先存入imageList數(shù)組中; }); } } } catch (error) { console.log(error); } // console.log('imageList', imageList); return imageList; }
3、解析文本內(nèi)容
(1)解析思路
- 使用FileReader對象來讀取excel文件數(shù)據(jù)data;
- 使用xlsx獲取excel表格數(shù)據(jù);
- 封裝sheetlist數(shù)據(jù)為el-table綁定的數(shù)據(jù)格式;
(2)解析過程
首先,查看FileReader的讀取結(jié)果;
async function beforeUpload(file){ // 解析圖片 imageList.value = await getExcelImage(file); // ==================================================================================== // // 解析數(shù)據(jù) let fileReader = new FileReader(); // 構(gòu)建fileReader對象 fileReader.readAsArrayBuffer(file); // 讀取指定文件內(nèi)容 // 讀取操作完成時 fileReader.onload = function (e) { try { console.log('data', e); // ...... } catch (e) { console.log("文件類型不正確"); return; } }; }
其次,使用XLSX.utils.sheet_to_json(worksheet, { header: 1 })方法,處理FileReader讀取到的數(shù)據(jù)data;
該方法的第一個參數(shù)woksheet是必須要傳的,值為工作表數(shù)據(jù);
第二個參數(shù)可選,用于指定輸出excel的格式;
// 加載按鈕的回調(diào) async function beforeUpload(file){ // 解析圖片 imageList.value = await getExcelImage(file); // ==================================================================================== // // 解析數(shù)據(jù) let fileReader = new FileReader(); // 構(gòu)建fileReader對象 fileReader.readAsArrayBuffer(file); // 讀取指定文件內(nèi)容 // 讀取操作完成時 fileReader.onload = function (e) { try { let data = e.target.result; // 取得數(shù)據(jù)data // console.log(data); let workbook = XLSX.read(data, { type: "binary" }); // 將data轉(zhuǎn)換成excel工作表數(shù)據(jù) // console.log("Excel工作簿", workbook); const worksheet = workbook.Sheets[workbook.SheetNames[0]]; // 獲取第一個工作表 // console.log("第一張工作表", worksheet); /* * XLSX.utils.sheet_to_json 輸出JSON格式數(shù)據(jù) * 獲取指定工作表中的數(shù)據(jù)sheetlist[],整個表中的數(shù)據(jù)存放在一個數(shù)組sheetlist中; * sheetlist數(shù)組中的每個元素均為一個數(shù)組rowlist,是每一行的數(shù)據(jù); */ const sheetlist = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); console.log(sheetlist,'sheetlist'); } catch (e) { console.log("文件類型不正確"); return; } }; }
到此為止,我們已經(jīng)獲取到了excel中的文本數(shù)據(jù)sheetlist,可以在控制臺查看如下所示結(jié)果;
外層是一個數(shù)組[ ],存放整個excel中的文本數(shù)據(jù);
其中的每個元素均為一個數(shù)組[ ],存放的是每一行的數(shù)據(jù);
最后,我們將這段解析excel中文本的代碼進行抽離;
// 加載按鈕的回調(diào) async function beforeUpload(file){ // 解析圖片 imageList.value = await getExcelImage(file); // 解析數(shù)據(jù) getExcelData(file); } // 解析數(shù)據(jù) function getExcelData(file) { let fileReader = new FileReader(); // 構(gòu)建fileReader對象 fileReader.readAsArrayBuffer(file); // 讀取指定文件內(nèi)容 // 讀取操作完成時 fileReader.onload = function (e) { try { let data = e.target.result; // 取得數(shù)據(jù)data // console.log(data); let workbook = XLSX.read(data, { type: "binary" }); // 將data轉(zhuǎn)換成excel工作表數(shù)據(jù) // console.log("Excel工作簿", workbook); const worksheet = workbook.Sheets[workbook.SheetNames[0]]; // 獲取第一個工作表 // console.log("第一張工作表", worksheet); /* * XLSX.utils.sheet_to_json 輸出JSON格式數(shù)據(jù) * 獲取指定工作表中的數(shù)據(jù)sheetlist[],整個表中的數(shù)據(jù)存放在一個數(shù)組sheetlist中; * sheetlist數(shù)組中的每個元素均為一個數(shù)組rowlist,是每一行的數(shù)據(jù); */ const sheetlist = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); // console.log('sheetlist', sheetlist); } catch (e) { console.log("文件類型不正確"); return; } }; }
4、封裝數(shù)據(jù)
根據(jù)上述內(nèi)容,excel文件中的圖片和文本內(nèi)容均已取得,如何封裝稱自己想要的數(shù)據(jù)格式,可以自行解決了;這里給出我自己在el-table中需要的數(shù)據(jù)格式封裝方法;
(1)封裝思路
獲取到的sheetlist的數(shù)據(jù)格式如下:
sheetlist = [ ["name", "age", "avatar"], ["張三", 18], ["李四", 20], ]
這并不是我們想要的,期望的格式應(yīng)該是如下所示:
最外層是一個數(shù)組[ ],里面是每個數(shù)據(jù)對象itemObj;
[ { name:'張三', age:18, avatar:"imageUrl" }, { name:'李四', age:20, avatar:"imageUrl" }, ]
首先,我們可以看出sheetlist[0],就是我們想要的el-table的列名;其次,使用雙重循環(huán)拆解重組數(shù)據(jù);
創(chuàng)建一個對象obj,在我們?nèi)〉絪heetlist[0][0],也就是“name”的時候,讓obj[“name”] = “張三”,obj[“age”] = “18”,obj[“avatar”] = “”,也就是obj[sheetlist[0][0]] = sheetlist[1][0]、obj[sheetlist[0][1]] = sheetlist[1][1],obj[sheetlist[0][2]] = sheetlist[1][2],以此類推...;
這里,剛剛好在給obj[“avatar”] = “”賦值的時候我們可以賦值成imagelist中的值,也就是obj[sheetlist[0][2]] = imagelist[i];注意,imagelist中存儲的是base64編碼值,別忘記賦值之前進行編碼轉(zhuǎn)換,obj[sheetlist[0][2]] = `data:image/png;base64,${imageList.value[i]};
最后,將新建的obj對象push進el-table綁定的數(shù)據(jù)源中即可看到結(jié)果了;
(2)實現(xiàn)過程
我們編寫一個封裝數(shù)據(jù)的方法,并在解析好excel的文本數(shù)據(jù)后調(diào)用它;
獲取el-table的列名;
// 封裝數(shù)據(jù) function formatDate(sheetlist) { tableColumnLabel.value = sheetlist[0]; // 獲取表格列名 }
此時,頁面上已經(jīng)有了顯示效果;
使用雙重循環(huán)拆解組合數(shù)據(jù),這里不做過多解釋,大家可以自己寫個方法,能夠讓我參考參考,感覺自己寫的也不怎么好;
// 封裝數(shù)據(jù) function formatDate(sheetlist) { tableColumnLabel.value = sheetlist[0]; // 獲取表格列名 for (let i = 0; i < sheetlist.length - 1; i++) { let obj = {}; for (let j = 0; j < sheetlist[0].length; j++) { // 頭像列,則取解析到的imageList中的相應(yīng)值; // 這里要求我們知道excel中存放圖片的列名,否則會導(dǎo)致讀取失??; if (sheetlist[0][j] == "avatar") { obj[sheetlist[0][j]] = `data:image/png;base64,${imageList.value[i]}`; // 注意base64編碼的處理 } else { // 非頭像列,直接取值; obj[sheetlist[0][j]] = sheetlist[i + 1][j] ?? ""; } } console.log(obj); } }
可以在控制臺查看每個obj對象的輸出結(jié)果;
最后,將這個obj對象push進el-table綁定的數(shù)據(jù)源中;
// 封裝數(shù)據(jù) function formatDate(sheetlist) { tableColumnLabel.value = sheetlist[0]; // 獲取表格列名 for (let i = 0; i < sheetlist.length - 1; i++) { // 這里length-1是因為我們的sheetlist[0]為列名,實際的數(shù)據(jù)要少 1; let obj = {}; for (let j = 0; j < sheetlist[0].length; j++) { // 頭像列,則取解析到的imageList中的相應(yīng)值; // 這里要求我們知道excel中存放圖片的列名,否則會導(dǎo)致讀取失敗; if (sheetlist[0][j] == "avatar") { obj[sheetlist[0][j]] = `data:image/png;base64,${imageList.value[i]}`; // 注意base64編碼的處理 } else { // 非頭像列,直接取值; obj[sheetlist[0][j]] = sheetlist[i + 1][j] ?? ""; } } // console.log(obj); tableData.value.push(obj); // 添加到el-table綁定的數(shù)據(jù)源中 } console.log("tableData.value", tableData.value); }
控制臺查看打印輸出結(jié)果;
頁面最終顯示效果;
5、現(xiàn)有問題
第一,采取這個方法的前提是得明確知道excel中存放圖片的列名;
第二,圖片是按照在excel中的順序加載的,存在較多未知的隱患;
第三,謹慎使用!謹慎使用!謹慎使用!
總結(jié)
到此這篇關(guān)于前端解析包含圖片的excel文件完整步驟及代碼示例的文章就介紹到這了,更多相關(guān)前端解析含圖片excel文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
移動端翻頁插件dropload.js(支持Zepto和jQuery)
這篇文章主要為大家詳細介紹了移動端翻頁插件dropload.js,支持Zepto和jQuery,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-07-07js使用generator函數(shù)同步執(zhí)行ajax任務(wù)
這篇文章主要為大家詳細介紹了js使用generator函數(shù)同步執(zhí)行ajax任務(wù),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09前端實現(xiàn)word文檔預(yù)覽和內(nèi)容提取的詳細過程
在前端直接讀取并原樣展示W(wǎng)ord文檔是一個相對復(fù)雜的任務(wù),因為Word文檔的格式(如.doc或.docx)與Web技術(shù)棧使用的格式(HTML、CSS)不兼容,下面這篇文章主要給大家介紹了關(guān)于前端實現(xiàn)word文檔預(yù)覽和內(nèi)容提取的詳細過程,需要的朋友可以參考下2024-05-05