其他分享
首页 > 其他分享> > Vue项目中excel表格文件的导入、导出

Vue项目中excel表格文件的导入、导出

作者:互联网

目录

使用到的一些构造函数、插件

FileReader

Blob

js-xlsx

ElementUI组件库

导入的excel文件预览

导入文件不带自定义弹窗

导入文件带自定义弹窗


使用到的一些构造函数、插件

FileReader

链接:https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader/FileReader

Blob

链接:https://developer.mozilla.org/zh-CN/docs/Web/API/Blob

Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。

想要读取Blob数据的唯一方法是FileReader。 FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 FileBlob 对象指定要读取的文件或数据。

js-xlsx

链接:https://github.com/SheetJS/sheetjs/tree/master/demos/vue

由SheetJS出品的js-xlsx是一款非常方便的只需要纯JS即可读取和导出excel的工具库,功能强大,支持格式众多,支持xls、xlsx、ods(一种OpenOffice专有表格文件格式)等十几种格式。

npm install xlsx  // 安装

import XLSX from 'xlsx'; // 导入

ElementUI组件库

链接:https://element.eleme.cn/#/zh-CN/component/installation

导入文件的弹窗使用到了el-dialog组件

导入的excel文件预览

导入文件不带自定义弹窗

<template>
    <div id='File'>
        <button class="file-btn" @click="handleUpBtn">
            <span>导入</span>
            <input type="file" ref="refFile" style="display: none" @change="handleFiles" >
        </button>
        <button class="file-btn" @click="handleDownBtn"><span>导出</span></button>
        <div class="container">
            <table class="main">
                <thead class="mainH">
                    <tr>
                        <td v-for="(item, index) in fileHeaders" :key="index">{{item}}</td>
                    </tr>
                </thead>
                <tbody class="mainB">
                    <tr v-for="(item, index) in fileData" :key="index+'1'">
                        <td v-for="(i, index) in item" :key="index+'2'">{{i}}</td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</template>

<script>
import XLSX from 'xlsx';
export default {
    name:'File',
    data(){
        return {
            file: "",
            fileName: "",
            fileData: [],
            fileHeaders: []
        }
    },
    methods: {
        // 上传文件
        handleUpBtn(){
            this.$refs.refFile.click(); // 触发文件按钮点击事件
        },

        // 在文件按钮发生变化后处理上传的文件数据
        handleFiles(){
            let selectedFile = this.$refs.refFile.files[0]; 
            // selectedFile中可以获取到文件名和文件大小 selectedFile.name, selectedFile.size
            this.file = selectedFile;
            this.fileName = selectedFile.name;
            /*
                在得到selectedFile文件之后,可以直接将selectedFile文件通过接口传递到后端,进行文件内容的处理
                如果文件需要在前端进行处理,就需要借助FileReader读取文件内容
                在前端可以进行一些简单的文件处理
                    比如判断文件的大小是否超出规定内容
                    判断文件的格式是否符合要求等
            */

            // 下面的操作对当前的文件进行读取、获取文件内容,可以借助XLSX将excel文件内容转成json格式的数据

            let reader = new FileReader();
            reader.readAsBinaryString(selectedFile); 
            /*
                FileReader共有4种读取方法:
                1.readAsArrayBuffer(file):将文件读取为ArrayBuffer。
                2.readAsBinaryString(file):将文件读取为二进制字符串
                3.readAsDataURL(file):将文件读取为Data URL
                4.readAsText(file, [encoding]):将文件读取为文本,encoding缺省值为'UTF-8'
            */

            reader.onload = (evt) => {
                try {
                    let data = evt.target.result;
                    let workbook = XLSX.read(data, { type: "binary" }); 
                    // 以二进制流方式读取得到整份excel表格对象  type: 'base64'
                    let wsname = workbook.SheetNames[0]; // 取第一张表 如果有多张数据表 可以使用循环遍历
                    let sheet = workbook.Sheets[wsname]; 
                    let jsonData = XLSX.utils.sheet_to_json(sheet); // 生成json表格内容
                    jsonData.map( item => { // 保存json数据
                        this.fileData.push(item); 
                    })
                    // 提取出表头还可以通过fileData提取,也可以使用下面的方式提取

                    // 保存表头数据 
                    let range = XLSX.utils.decode_range(sheet["!ref"]); 
                    /* 	
                    	sheet['!ref']表示所有单元格的范围,例如从A1到F8则记录为 A1:F8
                    	console.log(range); 
                    	控制台打印range 返回 {s:{c:9, r:10}, e:{c:0, r:0}} 
                    	表示表格内容最后一个单元格(10,9) 第一个单元格(0,0)
                    */
                    let col = range.s.c, row = range.s.r; // 10列 11行
                    for (col = range.s.c; col <= range.e.c; ++col) {
                        // 查找第一行中的单元格
                        let cell = sheet[XLSX.utils.encode_cell({ c: col, r: row })];
                        let cellH = cell ? XLSX.utils.format_cell(cell) : "无"; 
                        // 表单元格标题不存在则使用"无"代替
                        this.fileHeaders.push(cellH);
                    }
                    this.$message.success("导入成功!");
                } catch(err) {
                    console.error("出错了!", err);
                    return;
                }  
            };
        },

        // 导出文件
        handleDownBtn(){
            /*
                在点击导出按钮的时候,在这里请求后台接口,后台可能返回的文件数据是文件流数据,需要使用Blob进行解析
                axios({
                    url: 'http://localhost:3001/saveImg',
                    method: 'get',
                    responseType: 'blob',
                    // 可以传递一些params
                }).then( res => {
                    // 数据处理 res中包含了blob流文件
                })
            */

            // 使用blob解析流文件
            let blob = new Blob([this.file],{type: "application/vnd.ms-excel;charset=utf-8"}); 
            /* 
            	一般在请求后台返回文件的接口中传入了responseType: 'blob',就不用再new Blob了,直接使用返回的blob流文件
            	生成blob地址,下载文件即可
            	new Blob([], {type: xxx})  中的type有以下几种设置
            		type:"application/vnd.ms-excel"
                    type:"application/octet-stream;charset=utf-8"
                    type: "application/vnd.ms-excel;charset=utf-8"
                    type:"application/json"
                    type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"
                    type:"text/plain"
                    type:"image/jpeg"
                    type:"application/msword"
                    type:"application/pdf"
            */
            let objectUrl = URL.createObjectURL(blob); // 将blob对象转为blob地址
            /* 
            	使用a连接触发下载文件
            	download为下载的文件重新指定一个文件名,href为链接的文件地址,即为url
            	download属性很重要,它可以指定下载文件名,并且可以告诉浏览器目标链接是一个下载链接,不是一个普通链接
            	download也可以不给值,这样就会使用默认的文件名
            */
            let a = document.createElement('a');
            a.download = this.fileName;
            a.href = objectUrl;
            document.body.appendChild(a);
            a.click();
            // 释放内存
            URL.revokeObjectURL(objectUrl);
            document.body.removeChild(a);
            this.$message.success("导出成功!");
            
            /*
            使用a标签模拟js触发单击事件 a.click() 有兼容性问题,使用以下方法解决兼容性问题
            let a = document.createElement('a');
            a.href = url;
            a.download = saveName || ''; 
            // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效
            let event;
            if(window.MouseEvent) event = new MouseEvent('click');
            else {
            	event = document.createEvent('MouseEvents');
             	event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
            }
            a.dispatchEvent(event);
            */
        },
    }
}

</script>

<style scoped>
#File {
    margin: 100px 50px;
    background-color: #f1f2f0;
}
.file-btn {
    margin: 10px;
    padding: 5px 10px;
    border: 0;
    color: #ffffff;
    background-color: #4bcffa;
}
.file-btn:hover,
.select-file:hover {
    cursor: pointer;
}
.container {
    height: 300px;
    background-color: #f5f6fa;
    overflow: auto;
    padding: 10px;
}
.container .main {
    width: 100%;
    height: 100%;
    text-align: center;
}
</style>

导入文件之后的fileData数据(控制台打印)

 

 保存表头数据中的range数据(控制台打印)

 

 最终展示效果

导出文件

点击导出按钮,借助a链接下载文件

导入文件带自定义弹窗

<template>
    <div id='File'>
        <button class="file-btn" @click="dialogVisible=true">导入</button>
        <button class="file-btn" @click="handleDownBtn">导出</button>
        <!-- 展示数据 -->
        <div class="container">
            <table class="main" ref="table">
                <thead class="mainH">
                    <tr>
                        <td v-for="(item, index) in fileHeaders" :key="index">{{item}}</td>
                    </tr>
                </thead>
                <tbody class="mainB">
                    <tr v-for="itemObj in fileData" :key="itemObj.id">
                        <td v-for="(item, index) in itemObj.data" :key="index">{{item}}</td>
                    </tr>
                </tbody>
            </table>
        </div>
        <!-- 上传文件弹出框(elementUI组件) -->
        <el-dialog title="选择要上传的文件" :visible.sync="dialogVisible" width="50%" @close="dialogVisible=false" >
            <el-button type="primary" @click="handleUpBtn">上传文件</el-button>
            <input type="file" ref="refFile" style="display: none" @change="handleFiles" >
            <!-- 这里显示选中的文件 -->
            <div class="fileName"><span>{{ fileName }}</span></div>
            <span slot="footer">
                <el-button @click="dialogVisible=false">取 消</el-button>
                <el-button type="primary" @click="sureUpFile">确 定</el-button>
            </span>
        </el-dialog>
    </div>
</template>

<script>
import XLSX from 'xlsx';
import { nanoid } from "nanoid";
export default {
    name:'File',
    data(){
        return {
            selectedFile: "", // 导入的文件
            fileName: "", // 导入的文件名
            fileData: [], // 导入的文件转成json格式的数据
            fileHeaders: [], // 导入的文件表格标题
            dialogVisible: false, // 控制弹窗显示和隐藏
            canUpFile: false, // 控制文件是否可以上传
        }
    },
    methods: {
        // 上传文件
        handleUpBtn(){
            this.$refs.refFile.click(); // 触发文件按钮点击事件
        },

        // 在文件按钮发生变化后处理上传的文件数据
        handleFiles(){
            /*
                在选择好文件之后,可以在这里对文件的格式,或者大小等作出判断
                比如导入的文件格式是否正确,文件的大小是否符合要求,
            */
            let selectedFile = this.$refs.refFile.files[0];
            // 没有导入文件或者导入文件的格式出错
            if(!selectedFile || !/\.(xls|xlsx)$/.test(selectedFile.name.toLowerCase())) {
                this.$message.warning("请导入xls或xlsx格式的文件!");
                this.canUpFile = false;
                this.fileName = "";
                return;
            }
            this.canUpFile = true;
            this.selectedFile = selectedFile;
            this.fileName = selectedFile.name;
        },

        // 弹窗中确定上传文件
        sureUpFile(){ // 将文件进行保存提交
            /*
            	开发过程中
                一般在点击"确定"按钮的这个时候将selectedFile文件通过接口传递给后台
                根据后台返回的内容作出判断,判断文件导入成功还是失败
                下面只是普通的将文件数据获取出来并转成json格式的数据,然后显示在页面上
                (真实的开发顺序是:
                    前端将文件通过接口传递到后台
                    如果文件导入成功,再进行一次接口请求,请求后台最新的数据,并将最新的数据渲染到页面)
            */
            
            if(!this.canUpFile) {
                this.$message.warning("请导入文件!");
                this.fileName = "";
                return;
            }
            this.dialogVisible = false; // 关闭弹窗
            let reader = new FileReader();
            reader.readAsBinaryString(this.selectedFile); 
            reader.onload = (evt) => {
                try {
                    let data = evt.target.result;
                    let workbook = XLSX.read(data, { type: "binary" }); 
                    let wsname = workbook.SheetNames[0];
                    let sheet = workbook.Sheets[wsname]; 
                    let jsonData = XLSX.utils.sheet_to_json(sheet);
                    if(jsonData.length == 0) {
                        this.$message.warning("不能导入空文件!");
                        this.fileName = "";
                        return;
                    }
                    this.fileData = [];
                    jsonData.map( item => {
                        let itemObj = {};
                        itemObj.id = nanoid();
                        itemObj.data = item;
                        this.fileData.push(itemObj); 
                    })
                    console.log(this.fileData);
                    
                    // 保存文件表格标题
                    let range = XLSX.utils.decode_range(sheet["!ref"]); 
                    let col = range.s.c, row = range.s.r;
                    for (col = range.s.c; col <= range.e.c; ++col) {
                        let cell = sheet[XLSX.utils.encode_cell({ c: col, r: row })];
                        let cellH = cell ? XLSX.utils.format_cell(cell) : "无"; 
                        this.fileHeaders.push(cellH);
                    }
                    this.$message.success("导入成功!");
                } catch(err) {
                    this.$message.error("导入失败!");
                    console.error("导入失败!", err);
                    return;
                }  
            };
        },

        // 导出文件
        handleDownBtn(){
            let blob = new Blob([this.selectedFile],{type: "application/vnd.ms-excel;charset=utf-8"}); 
            let URLURL = window.URL || window.webkitURL || window
            let objectUrl = URLURL.createObjectURL(blob);
            let a = document.createElement('a');
            a.download = this.fileName;
            a.href = objectUrl;
            a.style.display = "none"; 
            document.body.appendChild(a);
            a.click();
            URL.revokeObjectURL(objectUrl);
            document.body.removeChild(a);
            this.$message.success("导出成功!");
        },
    }
}

</script>

<style scoped>
#File {
    margin: 100px 50px;
    background-color: #f1f2f0;
}
.file-btn {
    margin: 10px;
    padding: 5px 10px;
    border: 0;
    color: #ffffff;
    background-color: #4bcffa;
}
.file-btn:hover,
.select-file:hover {
    cursor: pointer;
}
.container {
    height: 300px;
    background-color: #f5f6fa;
    overflow: auto;
    padding: 10px;
}
.container .main {
    width: 100%;
    height: 100%;
    text-align: center;
}
.fileName {
    width: 100%;
    height: 40px;
    line-height: 40px;
}
</style>

自定义导入弹窗

点击导入文件,先弹出弹窗,再借助<input type="file">选择上传的文件

 

 

标签:文件,Vue,selectedFile,excel,导入,let,file,type
来源: https://blog.csdn.net/yun_huangZJ/article/details/120126343