实现功能:
1.对上传的zip压缩包进行解压。
2.解析excel页签中内容,用于列表数据展示。
3.解决文件名中带有汉字时解析乱码问题。
4.上传excel附件,用于预览。
5.采用input中type="file"来实现上述功能
1.html
<div @click="upload">上传</div>
<!-- 加一层if判断的原因:防止上传一次之后,在没有刷新的前提下,再次点击上传,不触发上传功能。 -->
<div v-if="uploadShowFlag">
<input
type="file"
@change="performanceUploadProcess"
ref="inputer"
v-show="false"
/>
</div>
<!-- 是否确认上传弹框 -->
<!-- 此处使用的是Ant Design Vue 组件-->
<a-modal
v-model="determine "
title="上传确认"
@ok="sure"
@cancel="clockConfirm"
>
<p>获取文件信息成功,点击确定将进行上传</p>
</a-modal>
2.js
npm i jszip
npm i xlsx
npm i iconv-lite //解决文件名乱码
import XLSX from "xlsx";
import JsZip from "jszip";
import {uploadFile,configFileReader,arrayBufferToBase64,dataURLtoFile} from './index'
import iconv from "iconv-lite"
data() {
return {
uploadListZip: [],//压缩包解析后的excel汇总数据上传
uploadShowFlag: false,//上传按钮触发
determine: false,//是否确认上传弹框
zipList: [],//压缩包解析前文件列表
flagLength:'',//用于记录异步解读的条数
upFlagSum:0,//用于计数当前解读的文件为第几条
}
},
methods: {
//1.用VUE按钮代替input原生按钮并触发
folderButtonClick(){
//每次点击上传时,初始化数据。
this.uploadShowFlag = true;
this.resultList = [];
this.uploadListZip = [];
this.zipList = [];
this.flagLength = "";
this.upFlagSum = 0;
//因为之前加了if判断,属于动态创建销毁input,所以需要使用nextTick进行处理
this.$nextTick(()=>{
this.$refs.inputer.click();
})
},
//2.判断文件类型
performanceUploadProcess() {
const inputDOM = this.$refs.inputer;
const fileName = inputDOM.files[0].name;
const fileType = fileName.substr(fileName.lastIndexOf(".") + 1);
if (fileType === "zip") {
this.zipXlsxUpload();
} else {
this.$message.error("上传文件类型错误,请重新上传");
}
},
//3.上传ZIP压缩包,读取并处理其中XLSX数据相关方法--
zipXlsxUpload() {
const inputDOM = this.$refs.inputer;
var zip = new JsZip();
//解压zip
zip.loadAsync(inputDOM.files[0], {
//解决文件名乱码问题
decodeFileName: function (bytes) {
return iconv.decode(bytes, 'gbk');
}
}).then((file) => {
//excel文件列表
this.zipList = file.files;
//摘除非excel数据
let arr =[];
for (let key in this.zipList) {
if (/\.(xlsx)$/.test(key)) {
arr.push(this.zipList[key])
}
}
const asyncReaderList = arr.filter((r) => !r.dir && r.name.indexOf("MACOSX") === -1)
//因为id只存在于文件名,所以解析前进行需要的数据拼接
let asyncReaderListCopy = [];
asyncReaderList.forEach(item =>{
let obj ={
// 员工Id格式:文件夹名/12345678姓名.xlsx
employeeId : /\/(\d{8})(.*)\.xlsx/.exec(item.name)[1],
value : zip.file(item.name).async("uint8array"),
name : item.name
}
asyncReaderListCopy.push(obj)
})
//用于reader.onload异步调用计数
this.flagLength = asyncReaderListCopy.length;
asyncReaderListCopy.forEach(zipFile=>{
//用于取到promise中的数据
Promise.resolve(zipFile.value).then((item)=>{
//1>用于附件上传,预览功能
let base = arrayBufferToBase64(item);
// Base64 转 File 对象
const result = dataURLtoFile(base, zipFile.name);
// console.log(result,'最终解压后的File对象')
this.resultList.push(result);
//2>用于读取每个excel页签里的内容
const reader = new FileReader(zipFile.name);
reader.onload = (e) => {
this.onReaderLoad(e,zipFile.employeeId);
};
reader.readAsBinaryString(new Blob([item]));
})
})
}).catch((err) => {
// 请求失败
this.$message.error(err)
});
},
//4取出XLSX文件数据
onReaderLoad(e,employeeId) {
this.upFlagSum ++;
//等待所有数据转换完成后在打开弹框确认上传
if(this.flagLength == this.upFlagSum){
this.determine = true; //打开预览模态框
}
let excelData = e.content;
//设置时间读取格式1>.{ type: "binary" ,cellDates:true,cellText:false}
const workbook = XLSX.read(excelData, { type: "binary" ,cellDates:true,cellText:false});
if (workbook.Sheets) {
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
//设置时间读取格式2>.{ raw: false,dateNF: 'yyyy/mm/dd'}
const data = XLSX.utils.sheet_to_json(worksheet, { header: 1 ,raw: false,dateNF: 'yyyy/mm/dd'});
//将读取的数据存入对象
let entity = {
id:employeeId,//取自文件名的id
name: data[2][3],//姓名
evaluationDate: data
.filter((r) => r.length === 9 && r[6] === "日期:")
.map((r) => r[8])[0],//日期
( ... 更多数据 )
};
//将读取的数据,存入数组中,用于保存
this.uploadListZip.push(entity);
}
},
//取消
clockConfirm(){
this.uploadShowFlag = false;
},
//确定
pfMarkConfirm() {
this.uploadShowFlag = false;
this.determine = false;
//调取保存和上传接口
},
},
mounted() {
//记得初始化哦
configFileReader();
}
3.index.js(摘取的可以公用的方法)
//初始化解读文档
export function configFileReader() {
FileReader.prototype.readAsBinaryString = function (fileData) {
var binary = "";
var pt = this;
var reader = new FileReader();
reader.onload = function () {
var bytes = new Uint8Array(reader.result);
var length = bytes.byteLength;
for (var i = 0; i < length; i++) {
binary += String.fromCharCode(bytes[i]);
}
pt.content = binary;
pt.onload(pt); //页面内data取pt.content文件内容
};
reader.readAsArrayBuffer(fileData);
};
}
//uint8array转base64
export function arrayBufferToBase64(array) {
array = new Uint8Array(array);
var length = array.byteLength;
var table = ['A','B','C','D','E','F','G','H',
'I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X',
'Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n',
'o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3',
'4','5','6','7','8','9','+','/'];
var base64Str = '';
for(var i = 0; length - i >= 3; i += 3) {
var num1 = array[i];
var num2 = array[i + 1];
var num3 = array[i + 2];
base64Str += table[num1 >>> 2]
+ table[((num1 & 0b11) << 4) | (num2 >>> 4)]
+ table[((num2 & 0b1111) << 2) | (num3 >>> 6)]
+ table[num3 & 0b111111];
}
var lastByte = length - i;
if(lastByte === 1) {
let lastNum1 = array[i];
base64Str += table[lastNum1 >>> 2] + table[((lastNum1 & 0b11) << 4)] + '==';
} else if(lastByte === 2){
let lastNum1 = array[i];
var lastNum2 = array[i + 1];
base64Str += table[lastNum1 >>> 2]
+ table[((lastNum1 & 0b11) << 4) | (lastNum2 >>> 4)]
+ table[(lastNum2 & 0b1111) << 2]
+ '=';
}
return base64Str;
}
//base64流
export function dataURLtoFile(dataURL, fileName, fileType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
/**
* 注意:【不同文件不同类型】,例如【图片类型】就是`data:image/png;base64,${dataURL}`.split(',')
* 下面的是【excel文件(.xlsx尾缀)】的文件类型拼接
*/
const arr = `data:${fileType};base64,${dataURL}`.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
let blob = new File([u8arr], fileName, { type: mime })
return blob
}
如果您有更好的想法,欢迎留言讨论,共同进步!