导出(若依框架)

article/2025/6/26 15:07:41

导出(若依框架)

分析用户界面,以用户列表的导出为例

在这里插入图片描述

导出

前端代码

​ 点击导出按钮,触发函数handleExport,在该函数中调用exportUser,exportUser执行完毕后,再调用download方法下载。

exportUser执行完成后,后端会生成临时文件execl。再调用download下载该文件。

/** 导出按钮操作 */
handleExport() {const queryParams = this.queryParams;this.$confirm('是否确认导出所有用户数据项?', "警告", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning"}).then(function() {return exportUser(queryParams);}).then(response => {this.download(response.msg);})
},
// 导出用户
export function exportUser(query) {return request({url: '/system/user/export',method: 'get',params: query})
}
// 通用下载方法
export function download(fileName) {window.location.href = baseURL + "/common/download?fileName=" + encodeURI(fileName) + "&delete=" + true;
}

后端代码

完成导出共发起了两次请求。分别是生成文件和下载文件

技术:反射,注解

文件生成

主要分析以下几个方法。方法调用层级关系

在这里插入图片描述

export 导出的入口函数

调用userService.selectUserList方法查询需要导出的数据,再调用util.exportExcel生成execl文件。

@Log(title = "用户管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:user:export')")
@GetMapping("/export")
public AjaxResult export(SysUser user)
{List<SysUser> list = userService.selectUserList(user);// 创建 ExcelUtil<SysUser>对象,入参为 SysUser.class。ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);return util.exportExcel(list, "用户数据");
}
exportExcel

在exportExcel方法中调用了init方法和exportExcel方法

public AjaxResult exportExcel(List<T> list, String sheetName)
{this.init(list, sheetName, Type.EXPORT);return exportExcel();
}
init

在init中调用了createExcelField方法,主要完成对ExcelUtil类中的fields属性赋值。

public void init(List<T> list, String sheetName, Type type)
{if (list == null){list = new ArrayList<T>();}this.list = list;  // 需要导出的数据交给listthis.sheetName = sheetName; // 生成execl的sheet名称this.type = type;   // 类型(0:导出导入;1:仅导出;2:仅导入)createExcelField();  // 主要完成对 List<Object[]> fields 属性的赋值。createWorkbook();    // 创建 Workbook对象  Workbook wb = new SXSSFWorkbook(500)
}
createExcelField

该方法执行完成后,完成了对ExcelUtil对象中的LIst<Object[]> fields属性的赋值。fields存放了导出的信息。

在object[]数组,object[0]存放了java.lang.reflect.Field对象, object[1]存放了注解com.ruoyi.common.annotation.Excel对象。

从object[0]可以获取到字段名称等信息。 从object[1]中可以获取到导出到execl中的名称以及对该字段的值作何处理(如格式化)等信息。

private void createExcelField()
{this.fields = new ArrayList<Object[]>();List<Field> tempFields = new ArrayList<>();// clazz属性是创建ExcelUtil对象时,完成了对该属性的赋值。以用户导出为例,class=SysUser.class。// 获取该类和其父类的属性字段,存入到tempFields集合中。tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));// 遍历字段,过滤出符合规律的字段。// 规律:1. 如果该字段有注解@Excel,则将字段对象,和注解对象封装到object[]数组中,并add到fields集合中。// 规律:2. 如果该字段有@Excels注解,则从该注解对象中获取Excel[]数组进行遍历。并将字段对象和Excel对象封装后,add到		     //         fields集合中。for (Field field : tempFields){// 单注解if (field.isAnnotationPresent(Excel.class)){//  this.fields.add(new Object[] { field, attr });putToField(field, field.getAnnotation(Excel.class));}// 多注解if (field.isAnnotationPresent(Excels.class)){Excels attrs = field.getAnnotation(Excels.class);Excel[] excels = attrs.value();for (Excel excel : excels){putToField(field, excel);}}}this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());this.maxHeight = getRowHeight();
}
exportExcel 完成execl文件的生成
/*** 对list数据源将其里面的数据导入到excel表单* * @return 结果*/
public AjaxResult exportExcel()
{OutputStream out = null;try{// 算出一共有多少个sheet.  // list.size 需要导出的数据条数。 sheetSize = 65536double sheetNo = Math.ceil(list.size() / sheetSize);for (int index = 0; index <= sheetNo; index++){// 创建sheet页createSheet(sheetNo, index);// 产生一行,表头Row row = sheet.createRow(0);int column = 0;// 写入各个字段的列头名称// 遍历fields集合,该集合的元素为object[]类型。从os[1]中获取注解对象,创建表头信息。for (Object[] os : fields){Excel excel = (Excel) os[1];// 创建单元格,并赋值,完成表头的创建this.createCell(excel, row, column++);}// 如果为导出类型,调用fillExcelData方法填充excel数据。if (Type.EXPORT.equals(type)){// 填充数据fillExcelData(index, row);addStatisticsRow();}}// 生成文件名称String filename = encodingFilename(sheetName);// 生成的文件路径在application.yml配置 ( profile: D:/ruoyi/uploadPath)out = new FileOutputStream(getAbsoluteFile(filename));// 生成execl文件,此时生成的文件在服务端。wb.write(out);// 将生成的文件名称封装到AjaxResult对象中return AjaxResult.success(filename);}catch (Exception e){log.error("导出Excel异常{}", e.getMessage());throw new CustomException("导出Excel失败,请联系网站管理员!");}finally{// 省略}
}
fillExcelData 完成execl数据填充
/*** 填充excel数据* * @param index 序号* @param row 单元格行*/
public void fillExcelData(int index, Row row)
{// 以第一个sheet页为例  index = 0, 常量sheetSize=65536// startNo是数据开始下标int startNo = index * sheetSize;// endNo-1是数据结束下标int endNo = Math.min(startNo + sheetSize, list.size());for (int i = startNo; i < endNo; i++){// 创建行对象,每一个sheet页从第二行开始,第一行为标题行。row = sheet.createRow(i + 1 - startNo);// 得到导出对象.T vo = (T) list.get(i);int column = 0;// 遍历fieldsfor (Object[] os : fields){Field field = (Field) os[0];Excel excel = (Excel) os[1];// 设置实体类私有属性可访问field.setAccessible(true);// 将导出信息 和 数据对象 execl的行对象 交由 addCell处理。this.addCell(excel, row, vo, field, column++);}}
}
addCell 完成对行记录的填充。

创建单元格,填充单元格内容。

/*** 添加单元格 */
public Cell addCell(Excel attr, Row row, T vo, Field field, int column)
{Cell cell = null;try{// 设置行高row.setHeight(maxHeight);// 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.if (attr.isExport()){// 创建cellcell = row.createCell(column);int align = attr.align().value();cell.setCellStyle(styles.get("data" + (align >= 1 && align <= 3 ? align : "")));// 用于读取对象中的属性// 通过Object o = field.get(vo); 得到字段的属性对象。// 如果注解属性targetAttr为空,直接返回o.// 若果不为空,该字段是否有小数点为标准再做处理// 该方法详解见下文。Object value = getTargetValue(vo, field, attr);// 字段的日期格式String dateFormat = attr.dateFormat();// 读取内容转表达式 (如: 0=男,1=女,2=未知)String readConverterExp = attr.readConverterExp();// 分隔符,读取字符串组内容String separator = attr.separator();// 字典类型 (如: sys_user_sex)String dictType = attr.dictType();// 根据字段的处理策略。填充单元格。if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)){cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value));}else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)){cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator));}else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value)){cell.setCellValue(convertDictByExp(Convert.toStr(value), dictType, separator));}else if (value instanceof BigDecimal && -1 != attr.scale()){cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString());}else{// 设置列类型setCellVo(value, attr, cell);}addStatisticsData(column, Convert.toStr(value), attr);}}catch (Exception e){log.error("导出Excel失败{}", e);}return cell;
}
getTargetValue 获取bean中的属性值
/*** 获取bean中的属性值* * @param vo 实体对象* @param field 字段* @param excel 注解* @return 最终的属性值* @throws Exception*/
private Object getTargetValue(T vo, Field field, Excel excel) throws Exception
{// 通过反射获取字段的值。该值有可能是其他类的对象。Object o = field.get(vo);// 如果 excel.targetAttr()不为空,则说明o是其他类中的一个对象。if (StringUtils.isNotEmpty(excel.targetAttr())){// 获取注解 targetAttr属性(另一个类中的属性名称,支持多级获取,以小数点隔开)String target = excel.targetAttr();if (target.indexOf(".") > -1){// 如果该属性有小数点,分割为数组遍历。 // 多级获取逻辑 // 举例说明:A类中持有B类的对象,B类中持有C类的对象。 导出A类数据时,需要导出C类的一个属性值// 可以使用.隔开。String[] targets = target.split("[.]");for (String name : targets){o = getValue(o, name);}}else{// 如果不包含小数点,(o, target) o为其他类对象,target为该类中的字段名称值。 // getValue 是通过o.getClass获取class对象,通过target字段名称从class中获取到Filed对象。进而得到filed字段值// 例子:SysUser#dept字段。o = getValue(o, target);}}return o;
}/*** 以类的属性的get方法方法形式获取值* * @param o* @param name* @return value* @throws Exception*/
private Object getValue(Object o, String name) throws Exception
{if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)){Class<?> clazz = o.getClass();Field field = clazz.getDeclaredField(name);field.setAccessible(true);o = field.get(o);}return o;
}

小结

自定义注解,描述Bean字段在Execl的表现形式。(比如,字段的值否需要格式化,字段对应到表格中的列名称等等)

使用List<object[]> 存入类的Filed对象和注解对象。(比如导出的字段有10个,则该集合大小为10)

导出执行的大致逻辑:

根据注解信息完成List<object[]>集合的赋值。

根据导出的数据量,计算需要导出的sheet页。针对每一个sheet页进行处理

创建表头:遍历List<object[]>集合,通过object[1]得到字段的注解信息,创建sheet页表头。

填充数据: 根据sheet页数,计算对应的数据范围,循环创建行对象,根据循环下标,在数据集合中获取改行对应的数据对象。行中创建单元格对象,接着遍历List<object[]>集合,通过object[0]获取数据对象中属性值,通过object[1]>获取对该值的处理策略。处理完毕后,将值填充到单元格中。

文件下载

文件生成后,将生成的文件名称返回到前端,客户端在调用download方法,向后端发起下载请求。

fileDownload下载入口方法
/*** 通用下载请求* * @param fileName 文件名称* @param delete 是否删除*/
@GetMapping("common/download")
public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
{try{if (!FileUtils.checkAllowDownload(fileName)){throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));}String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);// 获取下载路劲String filePath = RuoYiConfig.getDownloadPath() + fileName;// 设置ContentTyp="application/octet-stream" 通用Mime类型response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);// 设置Content-disposition  FileUtils.setAttachmentResponseHeader(response, realFileName);FileUtils.writeBytes(filePath, response.getOutputStream());// 下载后是否删除该文件if (delete){FileUtils.deleteFile(filePath);}}catch (Exception e){log.error("下载文件失败", e);}
}

http://chatgpt.dhexx.cn/article/951UCCXT.shtml

相关文章

简单入门若依框架

前言 本篇博客仅对若依框架的使用进行简单的介绍&#xff0c;它和php的fastadmin框架非常类似&#xff0c;是可以根据数据库表自动的生成一个完整的管理后台。关于fastadmin框架&#xff0c;我在前面的博客中有过简单的介绍&#xff0c;感兴趣的朋友可以查看这篇博客。 一、下载…

若依框架AjaxResult改造适应Swagger接口文档

一、 概述 若依框架后端使用的响应对象AjaxResult&#xff0c;和Swagger存在不兼容问题&#xff0c;导致返回体即使使用了Swagger注解&#xff0c;但是Swagger接口文档中&#xff0c;不显示返回体的对象Swagger文档&#xff1a; 若依Gitee上&#xff0c;也存在此问题&#x…

若依框架数据库密码加密

1. 用 Druid 提供的方法加密密码&#xff0c;控制台会输出三对值&#xff0c;分别是&#xff1a;privateKey、publicKey、password public static void main(String[] args) {String password "password";try {ConfigTools.main(new String[]{password});} catch (…

IDEA若依框架新建模块

创建新的module 项目上鼠标右键 选择Maven项目 Parent选ruoyi 点击Finish 编辑RuoYi-Vue_Wisdom/ruoyi-wisdom/pom.xml 在<artifactId>ruoyi-wisdom</artifactId>下面增加 <dependencies><!-- 通用工具--><dependency><groupId>com…

若依框架登录去除验证码

若依框架自带的登录功能还是挺好用的。但是在开发过程中经过因在编写代码或者编写设计文档登录会话到期重新登录。重新登录就会出现验证码&#xff0c;一天如果要输入10次验证码也会浪费不少时间。 验证码这个功能在生产环境非常有必要&#xff0c;但是在开发模式完全没有价值…

若依框架解析

1.框架介绍 RuoYi是一个基于Java技术开发的后台管理系统&#xff0c;基于技术组合&#xff08;SpringBootVue&#xff09;,内置模块有&#xff1a;部门管理、角色用户、菜单即按钮授权、数据权限、系统参数、日志管理、代码生成、表单构建等。支持多数据源、支持分布式事务。 …

若依框架项目

若依框架介绍 ruoyi是一个基于Java技术开发的后台管理系统。 包括有前后端分离版、微服务版、App版。内置模块有&#xff1a;部门管理、角色用户、菜单即按钮授权、数据权限、系统参数、日志管理、代码生成、表单构建等。支持多数据源、支持分布式事务。 若依前后端分离项目搭…

若依框架的使用

目录 1.为什么使用若依框架 2.环境搭建 2.1下载代码 2.2 系统配置 1.为什么使用若依框架 若依是一个快速开发框架&#xff0c;基于springboot&#xff0c;集成了mybatis&#xff0c;spring security&#xff0c;redis等常用的框架&#xff0c;支持多语言&#xff0c;已经实…

若依框架RuoYi前后端分离项目导入IDEA及运行启动

首先需要在码云上下载RuoYi-vue解压到你的工作目录&#xff08;https://gitee.com/y_project/RuoYi-Vue&#xff09; 然后去nodejs官网下载nodejs 除了选择你的安装目录一直next&#xff08;查看环境变量是否配置成功&#xff09; 安装完成后&#xff0c;cmd中输入&#xff1a;…

ruoyi(若依)框架使用说明(前后端分离)

TOC 框架介绍 本身是一个优秀的简单的后台管理系统框架,前端(Vue,ElementUI),后端(Spring Boot、Spring Security、Redis & Jwt),本身可以直接拉下来直接使用.它的强大之处在于代码自动生成器的使用,可以根据数据库的表对应生成全套前后端代码,代码植入后可以直接使用,复…

使用若依框架RuoYi前后端分离版(超详细步骤)

下一客:若依框架代码生成工具–单表增删改查(超详细步骤) 一、若依框架RuoYi项目运行启动教程(详细步骤) 1.1 官网下载代码 第一步:简介 RuoYi-Vue是一款基于SpringBootVue的前后端分离极速后台开发框架。 官网链接 在线文档 第二步:下载源码 1.点击复制,将链接复制到IDEA中…

若依开源框架解析

1、框架介绍 RuoYi是一个基于Java技术开发的后台管理系统&#xff0c;目前官方同步在维护的有3个版本。 开源协议&#xff1a;MIT 解释&#xff1a;MIT是和BSD一样宽松的许可协议,作者只想保留版权,而无任何其他了限制.也就是说,你必须在你的发行版里包含原许可协议的声明,无论…

若依框架的入门介绍及使用

若依框架的入门介绍及使用 文章目录 引言1.什么是若依2 .使用若依2.1系统需求2.1下载并运行2.2配置数据库 MySQL、Reids2.2.1MySQL配置2.2.2Redis配置 2.3前端启动 3. 框架结构和模块3.1后端结构3.2前端结构3.3内置功能3.4其他功能和扩展3.4.1代码生成器 总结 引言 若依&#…

从零入门开源框架---若依(前后端分离版)

一、若依是什么&#xff1f; 若依它就是一个开源项目&#xff0c;别人写好的代码&#xff0c;我们拿来进行二次开发,它主要是做数据和权限管理系统。 二、使用背景 任何公司的各种大的项目必然需要一个后台权限管理系统&#xff0c;这是必然的&#xff0c;但是如果不想投入太多…

若依框架学习笔记

若依框架简介 若依框架简介若依框架版本介绍若依框架&#xff08;vue前后端分离版本&#xff09;首页系统管理用户管理角色管理菜单管理 系统监控系统工具表单构建 代码生成 若依框架简介 根据官网介绍若依框架就是一个后台管理框架&#xff0c;主要使用技术在官网上也有介绍我…

若依框架入门(前后端分离版本)

目录 一 若依下载以及配置启动 1.下载地址 2.数据库引入及配置 3.前端vue启动 4.登录及验证 二.关于若依框架的基本使用方法 1.自动生成 2.菜单的配置 3.数据字典 4.关于api接口 5.关于定时任务 6.关于分页 一 若依下载以及配置启动 1.下载地址 学习基础:Spring…

若依框架详细使用

目录 &#x1f3f3;‍&#x1f308;若依是用来干什么的❓ &#x1f6a9;技术支持&#xff1a; &#x1f3f3;‍&#x1f308;如何下载❓ &#x1f6a9;官网地址: &#x1f3f3;‍&#x1f308;如何搭建ruoyi环境❓ &#x1f6a9;若依框架的目录结构 &#x1f6a9; 修改配…

js元素距离和鼠标位置整理

距离 offsetTop / offsetLeft ( 元素与父级 定位元素/table/td/th/body 的距离 ) clientTop / clientLeft ( 元素边框宽度 ) scrollTop / scrollLeft ( 元素滚动距离 ) window.scrollY / window.scrollX ( 文档/页面 滚动距离 ) window.pageYOffset / window.pageXOffset ( 另…

原生js实现跑马灯效果,鼠标放下可以停止跑动

js原生代码跑马灯效果纯js代码如下&#xff1a; 1.首先html的内容&#xff08;里面图片自己可以换&#xff0c;li里面也可以填文字&#xff09; <div> <ul><li>呵呵呵呵呵3</li> <li>叽叽叽叽叽4</li> <li…

unity拖拽UI生成Cube并跟随鼠标移动,放置点可放下

代码&#xff1a; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; using UnityEngine.UI;public class DragCreate : MonoBehaviour {public GameObject Cube;public GameObje…