Vue中实现自定义excel下载
最近在工作中遇到一个需求,就是需要在前端实现一个错误模板Excel的下载功能。
实现下载有两种方式,一种是后端生成一个excel,放在服务器指定目录下,然后前端直接去后端拿。第二种是后端传给前端一个json的list,前端用后端传过来的json的list直接在前端合成一个excel。
第一种:后端生成excel
java后端生成excel代码
生成excel工具方法
/** * @author: wu linchun * @creat: 2021-07-29 9:49 * @desc: 将list集合转成Excel文件 * list: 内容list * path: 上传的excel路径 * fileName: 上传的excel名称 **/public static String createExcel(List<? extends Object> list, String path, String fileName) {String result = "";if (list.size() == 0 || list == null) {result = "没有对象信息";} else {Object o = list.get(0);Class<? extends Object> clazz = o.getClass();String className = clazz.getSimpleName();//这里通过反射获取字段数组Field[] fields = clazz.getDeclaredFields();File folder = new File(path);if (!folder.exists()) {folder.mkdirs();}String name = fileName.concat(".xls");WritableWorkbook book = null;File file = null;try {file = new File(path.concat(File.separator).concat(name));//创建xls文件book = jxl.Workbook.createWorkbook(file);WritableSheet sheet = book.createSheet(className, 0);//列int i = 0;//行int j = 0;for (Field f : fields) {j = 0;//这里把字段名称写入excel第一行中Label label = new Label(i, j, f.getName());sheet.addCell(label);j = 1;for (Object obj : list) {Object temp = getFieldValueByName(f.getName(), obj);String strTemp = "";if (temp != null) {strTemp = temp.toString();}//把每个对象此字段的属性写入这一列excel中sheet.addCell(new Label(i, j, strTemp));j++;}i++;}book.write();result = file.getPath();} catch (Exception e) {// TODO Auto-generated catch blockresult = "SystemException";e.printStackTrace();} finally {fileName = null;name = null;folder = null;file = null;if (book != null) {try {book.close();} catch (WriteException e) {// TODO Auto-generated catch blockresult = "WriteException";e.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blockresult = "IOException";e.printStackTrace();}}}}//最后输出文件路径return result;}
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
下载文件
/** * @author: wu linchun * @creat: 2021-07-29 13:28 * @desc: 下载错误模板 **/@Overridepublic ResponseEntity<Object> downloadErrorModel() throws FileNotFoundException {// 这里的fileName是指路径+文件名String fileName="move_cms/src/main/resources/static/errorList.xls";File file = new File(fileName);InputStreamResource resource = new InputStreamResource(new FileInputStream((file)));org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();headers.add("Content-Disposition", String.format("attachment;filename="%s"", file.getName()));headers.add("Cache-Control", "no-cache,no-store,must-revalidate");headers.add("Pragma", "no-cache");headers.add("Expires", "0");ResponseEntity<Object> responseEntity = ResponseEntity.ok().headers(headers).contentLength(file.length()).contentType(MediaType.parseMediaType("application/text")).body(resource);return responseEntity;}
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
下载excel的接口
/** * @author: wu linchun * @creat: 2021-07-29 11:41 * @desc: 下载错误模板 **/@ApiOperation(value = "下载错误模板")@GetMapping(value = "/downloadErrorModel")@PassTokenpublic ResponseEntity<Object> downloadErrorModel() throws FileNotFoundException {System.out.println("下载错误模板");return welfareGrantService.downloadErrorModel();}
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
在掉/downloadErrorModel这个接口是要注意,只能用超链接 “<a:href />调用,不能使用axios调这个接口。这是由于axios获取的是json
而这个接口返回的是file类型,file会以byte流的形式在http上面传输,因此调/downloadErrorModel这个接口将会接收到byte流,axios默认是接收不了byte的,需要进行一些特殊的设置。可以参考一下这篇:vue使用axios接收流文件_weixin_43869439的博客-CSDN博客
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
如果使用超链接的方式访问接口进行文件下载的话,则需要获取到后端服务器的ip+port (实际中不建议这么做,因为get请求会暴露ip地址和端口,可通过浏览器f12看到ip地址和端口,不安全)
ip地址可通过 InetAddress.getLocalHost()获取到,端口号直接用@Value从配置文件中拿到。
第二种:前端合成excel
在vue工程中引入两个依赖:file-saver和xlsx
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
使用“npm install”,安装package.json中新增的依赖
npm install
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
新增 ExportExcelUtil.js,用来根据list生成excel
/* eslint-disable */
import { saveAs } from 'file-saver'
import XLSX from 'xlsx'function generateArray(table) {var out = [];var rows = table.querySelectorAll('tr');var ranges = [];for (var R = 0; R < rows.length; ++R) {var outRow = [];var row = rows[R];var columns = row.querySelectorAll('td');for (var C = 0; C < columns.length; ++C) {var cell = columns[C];var colspan = cell.getAttribute('colspan');var rowspan = cell.getAttribute('rowspan');var cellValue = cell.innerText;if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;//Skip rangesranges.forEach(function (range) {if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);}});//Handle Row Spanif (rowspan || colspan) {rowspan = rowspan || 1;colspan = colspan || 1;ranges.push({s: {r: R,c: outRow.length},e: {r: R + rowspan - 1,c: outRow.length + colspan - 1}});};//Handle ValueoutRow.push(cellValue !== "" ? cellValue : null);//Handle Colspanif (colspan)for (var k = 0; k < colspan - 1; ++k) outRow.push(null);}out.push(outRow);}return [out, ranges];
};function datenum(v, date1904) {if (date1904) v += 1462;var epoch = Date.parse(v);return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}function sheet_from_array_of_arrays(data, opts) {var ws = {};var range = {s: {c: 10000000,r: 10000000},e: {c: 0,r: 0}};for (var R = 0; R != data.length; ++R) {for (var C = 0; C != data[R].length; ++C) {if (range.s.r > R) range.s.r = R;if (range.s.c > C) range.s.c = C;if (range.e.r < R) range.e.r = R;if (range.e.c < C) range.e.c = C;var cell = {v: data[R][C]};if (cell.v == null) continue;var cell_ref = XLSX.utils.encode_cell({c: C,r: R});if (typeof cell.v === 'number') cell.t = 'n';else if (typeof cell.v === 'boolean') cell.t = 'b';else if (cell.v instanceof Date) {cell.t = 'n';cell.z = XLSX.SSF._table[14];cell.v = datenum(cell.v);} else cell.t = 's';ws[cell_ref] = cell;}}if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);return ws;
}function Workbook() {if (!(this instanceof Workbook)) return new Workbook();this.SheetNames = [];this.Sheets = {};
}function s2ab(s) {var buf = new ArrayBuffer(s.length);var view = new Uint8Array(buf);for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;return buf;
}export function export_table_to_excel(id) {var theTable = document.getElementById(id);var oo = generateArray(theTable);var ranges = oo[1];/* original data */var data = oo[0];var ws_name = "SheetJS";var wb = new Workbook(),ws = sheet_from_array_of_arrays(data);/* add ranges to worksheet */// ws['!cols'] = ['apple', 'banan'];ws['!merges'] = ranges;/* add worksheet to workbook */wb.SheetNames.push(ws_name);wb.Sheets[ws_name] = ws;var wbout = XLSX.write(wb, {bookType: 'xlsx',bookSST: false,type: 'binary'});saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx")
}export function export_json_to_excel({multiHeader = [],header,data,filename,merges = [],autoWidth = true,bookType = 'xlsx'
} = {}) {/* original data */filename = filename || 'excel-list'data = [...data]data.unshift(header);for (let i = multiHeader.length - 1; i > -1; i--) {data.unshift(multiHeader[i])}var ws_name = "SheetJS";var wb = new Workbook(),ws = sheet_from_array_of_arrays(data);if (merges.length > 0) {if (!ws['!merges']) ws['!merges'] = [];merges.forEach(item => {ws['!merges'].push(XLSX.utils.decode_range(item))})}if (autoWidth) {/*设置worksheet每列的最大宽度*/const colWidth = data.map(row => row.map(val => {/*先判断是否为null/undefined*/if (val == null) {return {'wch': 10};}/*再判断是否为中文*/else if (val.toString().charCodeAt(0) > 255) {return {'wch': val.toString().length * 2};} else {return {'wch': val.toString().length};}}))/*以第一行为初始值*/let result = colWidth[0];for (let i = 1; i < colWidth.length; i++) {for (let j = 0; j < colWidth[i].length; j++) {if (result[j]['wch'] < colWidth[i][j]['wch']) {result[j]['wch'] = colWidth[i][j]['wch'];}}}ws['!cols'] = result;}/* add worksheet to workbook */wb.SheetNames.push(ws_name);wb.Sheets[ws_name] = ws;var wbout = XLSX.write(wb, {bookType: bookType,bookSST: false,type: 'binary'});saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), `${filename}.${bookType}`);
}
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
在.vue中添加下载excel方法
// 下载ExceldownloadExcel () {import('@/utils/ExportExcelUtil').then(excel => {const filterVal = ['item', 'error']const tHeader = ['item', 'error']const data = this.formatModelJson(filterVal, this.errList)console.log(data)console.info(data)excel.export_json_to_excel({header: tHeader,data,filename: 'errorList'})})},// 格式化JSONformatModelJson (filterVal, list) {return list.map(v => filterVal.map(j => {return v[j]}))},
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
errorList是从后端传过来的
exportExcel () {getErrorListExcel().then(response => {this.errList = response.data})}
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
使用created方法,在加载.vue时,就调用/getErrorListExcel接口,获取errorList的值。
created () {this.exportExcel()},
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
调用后端接口的api
import axios from 'axios'export function getErrorListExcel () {return axios.get('http://127.0.0.1:8082/login/getErrorListExcel', {}).then(response => response).catch(error => error)
}
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
后端接口设置errorList中的值
@GetMapping("/getErrorListExcel")public List<ErrorItemBO> getErrorListExcel() {List<ErrorItemBO> list = new ArrayList<>();for (int i = 0; i < 10; i++) {ErrorItemBO errorItemBO = new ErrorItemBO();errorItemBO.setItem("item" + i);errorItemBO.setError("error" + i);list.add(errorItemBO);}return list;}
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
<img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />
总结
如果涉及到文件下载,尽量后端传一个list,然后都在前端合成相应的文件以到达减轻服务器负担的作用。
最后
最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。
有需要的小伙伴,可以点击下方卡片领取,无偿分享
Vue中实现自定义excel下载相关推荐
- vue中svg转png下载
vue中svg转png下载 <template><div><button @click="downloadPng">change</but ...
- Vue学习笔记:Vue中封装自定义步骤条 实现上下一步
Vue中封装自定义步骤条 实现上下一步 效果图: 如上图:在VUE中实现效果,VUE+Element,ant都有封装好的UI,直接引用就好了: 这里,觉得样式不符合UI设计,所以自定义封装了一个步骤条 ...
- vue导入音乐_【vlog制作】不经电脑,如何在VUE中导入自定义音乐
VUE按不同风格.节气/节日.每月精选和流行曲集设置了多个音乐库,用户在剪辑视频的时候可以在库中挑选BGM,省掉了找音乐的麻烦,对于新手用户尤为贴心.然而对于频繁使用VUE剪辑视频的用户来说,原配音乐 ...
- 「后端小伙伴来学前端了」关于Vue中的自定义事件,组件绑定自定义事件实现通信
傍晚的月亮 前言 原本这篇打算写Vue中的那个全局事件总线的原理,但是发现自己少写了这个自定义事件,不讲明白这个自定义事件的操作,不好写全局事件原理,于是就有了这篇文章拉. 一.v-on指令 要讲自定 ...
- vue中如何进行Excel文件的下载
文章目录 业务需求: 实现方式 A1.模板下载: A2.搜索下载: A3.勾选下载: A4.上传错误记录下载Excel文件 业务需求: Q1.文件批量导入前,需要按照指定的格式与内容上传Excel文件 ...
- vue中使用a标签下载静态资源文件(比如excel、pdf等)后端不参与
问题描述 平常我们在项目中需要做下载文件的功能,比如下载excel表格.比如下载pdf文件.比如下载word文档,这样的功能,一般是后端做,即:后端返回一个流文件.或者返回一个url地址- 但是,实际 ...
- vue中后端做Excel导出功能返回数据流前端如何做处理
项目中有一个导出功能的实现,用博客来记录一下.因为需求对导出表格的数据格式和样式有要求,所以这个导出功能放到后端来做,而且后端返回的是数据流,所以需要处理成想要的表格并导出来. 先看下效果图: 页面效 ...
- vue中通过自定义指令将汉字转化为首字母大写、首字母小写、大写、小写的拼音
使用情景: 在文本框中输入内容,例如姓名 在页面中将姓名转化为姓名的拼音,包括大写拼音.小写拼音.首字母大写拼音.大写拼音缩写.小写拼音缩写 新建一个 pinyin.js 文件 这是一串又臭又长的un ...
- Vue.js + axios 实现excel下载
在以前实现的下载excel功能中,都是用 <a href="xxx">DOWNLOAD</a> 来实现的. 但是由于项目中采用了token验证,上述的做法就 ...
最新文章
- 流程图函数’怎么画_原来函数可以这么美!
- 用 Python 爬了点你们喜欢的小电影
- 超级干货:3个性能监控和优化命令详解
- 在AWS Lambda上运行脚本语言:PHP、Ruby和Go(外文翻译)
- Javascript 严格模式详解
- python多个分隔符分割字符串_Python中带有多个分隔符的拆分字符串
- APAC SharePoint Conference 2007 讲义与资源下载
- MySQL安装后默认自带数据库的作用
- 数组方法 splice()与slice()的区别
- 详细记录基于vue+nodejs+mongodb构建的商城学习(四)基于项目的node.js开发后端的学习与梳理总结...
- matlab中的m-file,MATLAB m-file帮助格式化
- centos操作系统镜像文件下载
- Qt之SQLite数据库可视化工具
- OAuth 2.0授权框架详解
- js html post提交表单,JS动态创建表单post提交
- 公摊面积用计算机怎么计算,公摊面积计算(公摊面积计算器)
- 纸牌游戏——小猫钓鱼(队列、栈)
- 【微动弹性带方法——续鞍点】
- 【uniappAPP实现支付宝授权登录】
- PADS如何导入DXF文件
热门文章
- 6. FileInputFormat实现类
- 如今是云散雪消花残月缺
- 架设单位内部FTP服务器
- 神经网络、智能芯片、神经计算、飞行控制相关调研
- 爬虫学习总结——————血狱魔帝
- jQuery如何移除css样式?
- halcon脚本-螺丝孔中间小黑圆位置【附源码】
- AI绘画发展史(伪):从免费到吃屎;YSDA·自然语言处理课程8K Star;伯克利CS285·深度强化学习课程;前沿论文 | ShowMeAI资讯日报
- 详解android framework中StateMachine(HSM层次状态机)的实现
- 谷歌浏览器的打印功能