vue中实现:解压zip,解析excel页签内容,并实现附件上传

51 篇文章 1 订阅
30 篇文章 1 订阅

实现功能:
     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       
}

如果您有更好的想法,欢迎留言讨论,共同进步!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值