若依-导出后端解析
针对若依框架微服务版本学习
若依导入导出功能的具体使用详见:后台手册 | RuoYi
1.导出逻辑:
导出文件的逻辑是先创建一个临时文件,等待前端请求下载结束后马上删除这个临时文件。但是有些下载插件,例如迅雷(他们是二次下载),这个时候文件已经删除,会导致异常,找不到文件。可强制把所有的导出都改成流的形式返回给前端,不采用临时文件的方法,具体方法后台手册都有(目前下载的代码为流的形式)
2.代码详解
export 导出的入口函数
1.controller层
调用userService.selectUserList(user)方法查询数据,util.exportExcel()方法以流的形式返回前端文件
@Log(title = "用户管理", businessType = BusinessType.EXPORT)@RequiresPermissions("system:user:export")@PostMapping("/export")public void export(HttpServletResponse response, SysUser user){List<SysUser> list = userService.selectUserList(user);
// 创建 ExcelUtil<SysUser>对象,入参为 SysUser.classExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);util.exportExcel(response, list, "用户数据");}
2.调用ExcelUtil类
/*** 对list数据源将其里面的数据导入到excel表单** @param response 返回数据* @param list 导出数据集合* @param sheetName 工作表的名称* @param title 标题* @return 结果*/public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title){
//告诉浏览器或客户端,响应的内容是一个 Excel 文件(XLSX 格式)通常用于文件下载response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
//设置 HTTP 响应的字符编码为 UTF-8response.setCharacterEncoding("utf-8");
//调用init方法对数据处理this.init(list, sheetName, title, Type.EXPORT);
//对list数据源将其里面的数据导入到excel表单exportExcel(response);}
1.init方法
public void init(List<T> list, String sheetName, String title, Type type){
// 需要导出的数据交给listif (list == null){list = new ArrayList<T>();}this.list = list;
// 生成execl的sheet名称this.sheetName = sheetName;
// 类型(0:导出导入;1:仅导出;2:仅导入)this.type = type;
//赋值标题this.title = title;
// 主要完成对 List<Object[]> fields 属性的赋值。createExcelField();
// 创建一个 Workbook 工作簿对象 createWorkbook();
//创建excel第一行标题createTitle();//创建对象的子列表名称createSubHead();}
1.createExcelField();
private void createExcelField(){//获取字段注解信息this.fields = getFields();//通过 Java 的 Stream API 对 fields 进行排序this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());//根据注解获取最大行高this.maxHeight = getRowHeight();}/*** 获取字段注解信息*/public List<Object[]> getFields(){List<Object[]> fields = new ArrayList<Object[]>();List<Field> tempFields = new ArrayList<>();//获得传入实体类的父类的所有声明字段tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));//获得传入实体类所有声明字段tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));//是否需要自定义显示属性列showColumn设置,详见官网文档if (StringUtils.isNotEmpty(includeFields)){//遍历临时字段集合for (Field field : tempFields){//判断字段是否在自定义显示属性列中//判断字段是否有@Excels注解if (ArrayUtils.contains(this.includeFields, field.getName()) || field.isAnnotationPresent(Excels.class)){// 添加字段信息addField(fields, field);}}}//hideColumn()自定义隐藏Excel中列属性else if (StringUtils.isNotEmpty(excludeFields)){for (Field field : tempFields){if (!ArrayUtils.contains(this.excludeFields, field.getName())){addField(fields, field);}}}else{for (Field field : tempFields){addField(fields, field);}}return fields;}/*** 添加字段信息*/public void addField(List<Object[]> fields, Field field){// 单注解if (field.isAnnotationPresent(Excel.class)){//获得注解实例Excel attr = field.getAnnotation(Excel.class);if (attr != null && (attr.type() == Type.ALL || attr.type() == type)){//创建一个包含两个元素的数组,其中数组的类型是 Object,元素类型会根据它们的实际类型而定fields.add(new Object[] { field, attr });}//判断field的类型是否是collection集合类型if (Collection.class.isAssignableFrom(field.getType())){//获得对象的子列表方法subMethod = getSubMethod(field.getName(), clazz);// getGenericType()方法获取某个字段的泛型类型的方法ParameterizedType pt = (ParameterizedType) field.getGenericType();//getActualTypeArguments()方法从一个泛型类型中获取实际的类型参数,并将其转换为 Class<?> 类型Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];//通过FieldUtils.getFieldsListWithAnnotationf()方法中反射,找到类subClass中所有带有Excel注解的字段,并将它们存储在 subFields 变量this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);}}// 多注解if (field.isAnnotationPresent(Excels.class)){Excels attrs = field.getAnnotation(Excels.class);Excel[] excels = attrs.value();for (Excel attr : excels){if (StringUtils.isNotEmpty(includeFields)){if (ArrayUtils.contains(this.includeFields, field.getName() + "." + attr.targetAttr())&& (attr != null && (attr.type() == Type.ALL || attr.type() == type))){fields.add(new Object[] { field, attr });}}else{if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr())&& (attr != null && (attr.type() == Type.ALL || attr.type() == type))){fields.add(new Object[] { field, attr });}}}}}/*** 获取对象的子列表方法* * @param name 名称* @param pojoClass 类对象* @return 子列表方法**///通过字段名(name)获取给定类(pojoClass)的 getter 方法。// 这个方法根据字段名生成对应的 getter 方法名称,然后使用反射来查找该方法public Method getSubMethod(String name, Class<?> pojoClass){//将字段名 name 转换为标准的 getter 方法名StringBuffer getMethodName = new StringBuffer("get");//将字段的第一个字母转为大写getMethodName.append(name.substring(0, 1).toUpperCase());//提取 name 字符串从索引位置 1 开始到字符串结束的部分getMethodName.append(name.substring(1));Method method = null;try{//pojoClass.getMethod() 方法通过反射查找指定的方法。// getMethodName.toString() 返回的是动态生成的 getter 方法名//new Class[] {} 表示该方法不接受任何参数(即没有输入参数)method = pojoClass.getMethod(getMethodName.toString(), new Class[] {});}catch (Exception e){log.error("获取对象异常{}", e.getMessage());}return method;}
1.clazz.getSuperclass().getDeclaredFields()
clazz:是一个 Class 类型的对象,它表示一个 Java 类。
getSuperclass():是 Class 类中的方法,返回该类的父类(即继承自哪个类)。如果该类没有父类(例如 Object 类),则返回 null。
getDeclaredFields():是 Class 类中的方法,返回该类所有声明的字段,包括私有字段和保护字段等(不包括继承的字段)。
例如:Dog类继承Animal类
clazz.getSuperclass() 获取的是 Dog 类的父类 Animal 类。
getDeclaredFields() 获取了 Animal 类中声明的字段(name、age 和 species),它们的访问修饰符分别是 private、protected 和 public。
getDeclaredFields() 不会返回父类继承的字段,如果需要获取父类的字段(包括继承的),可以使用 getFields()。
getDeclaredFields() 回的是类中所有声明的字段,包括 private、protected、public 和默认访问权限(包私有)的字段。Java的private字段访问权限是受保护的,即使反射获取到私有字段,也无法直接访问它,需要使用 setAccessible(true) 来允许访问该字段
2.public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
ava 反射 API 中的方法,用于检查某个字段是否被特定的注解所标注。它属于 java.lang.reflect.Field 类。通过该方法,你可以判断某个字段是否使用了某个注解
对于反射操作非常有用,尤其是在需要处理动态注解或者特定配置时
3.public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
field.getAnnotation 是 Java 反射 API 中 java.lang.reflect.Field 类的一个方法,用于获取指定字段上的特定注解实例。与 isAnnotationPresent 方法不同,getAnnotation 返回注解的实例,如果字段上没有该注解,则返回 null。
4.public boolean isAssignableFrom(Class<?> cls)
Java 中 Class 类的一个方法,用于判断某个类或接口的对象是否可以赋值给另一个类或接口的变量。它在反射或类型检查的场景中非常常见。
检查当前类是否可以接受传入类的实例(即目标类的对象能否赋值给当前类的变量)
参数:
cls:要与当前类进行比较的类或接口的 Class 对象。
返回值:
如果当前类可以接受 cls 类型的实例(即 cls 类型的对象可以赋值给当前类的引用变量),则返回 true。
否则,返回 false。用于在运行时进行类型兼容性检查的工具,特别适用于需要动态判断类型关系的情况
类型检查:检查某个类或接口是否可以赋值给另一种类型的变量。
反射:在使用反射时,可能需要验证某个对象是否可以赋给特定类型的变量。
动态类型检查:在运行时检查一个对象能否安全地被强制转换为另一类型,避免出现 ClassCastException报错。
5.ParameterizedType pt = (ParameterizedType) field.getGenericType();
getGenericType() 是用于获取某个字段(Field 对象)的泛型类型的方法。返回一个 Type 对象
常见的返回类型:
原始类型: 如果字段没有泛型,getGenericType() 返回的 Type 是该字段的原始类型(例如 int, String 等)。
ParameterizedType: 如果字段的类型是一个带有泛型的类型(如 List<String>),getGenericType() 返回的是 ParameterizedType,它表示该类型的具体泛型参数。
GenericArrayType: 如果字段是泛型数组类型(如 T[] 或 List<String>[]),则返回 GenericArrayType。ParameterizedType:ParameterizedType 是 Type 接口的一个实现,表示带有类型参数的泛型类型
2.createWorkbook()
/*** 创建一个工作簿*/public void createWorkbook(){// 创建一个SXSSFWorkbook工作簿,最大行数为500this.wb = new SXSSFWorkbook(500);// 在工作簿中创建一个工作表(Sheet)this.sheet = wb.createSheet();// 设置工作表的名称wb.setSheetName(0, sheetName);// 创建样式this.styles = createStyles(wb);}
1.SXSSFWorkbook类
SXSSFWorkbook是Apache POI库中的一个类,专门用于处理大型 Excel 文件。它支持在内存中流式写入数据,而不是将整个工作簿保存在内存中,因此可以处理非常大的文件,而不容易导致内存溢出。采用“滚动式写入”技术,将数据按需写入磁盘500作为构造函数的参数表示在内存中同时保留的最大行数。在这个例子中,它设置每次最多将 500 行加载到内存中,超出 500 行的数据将被写入磁盘。
3.createTitle()
/*** 创建excel第一行标题*/public void createTitle(){if (StringUtils.isNotEmpty(title)){//计算标题行的最后一列的列号,默认是 fields 列表的大小减去 1int titleLastCol = this.fields.size() - 1;//判断是否有对象的子属性列表if (isSubList()){// 如果有子属性列表,标题的最后一列索引要加上子属性的大小titleLastCol = titleLastCol + subFields.size() - 1;}//创建一个新的行,作为标题行Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);// 设置标题行的高度为30磅titleRow.setHeightInPoints(30);//在标题行的第一个单元格(列索引为 0)创建一个单元格对象。这是标题文本所在的单元格Cell titleCell = titleRow.createCell(0);//设置标题单元格的样式titleCell.setCellStyle(styles.get("title"));//设置标题单元格的内容为 titletitleCell.setCellValue(title);//合并单元格:通过 sheet.addMergedRegion() 方法合并标题行的单元格sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), 0, titleLastCol));}}
4.createSubHead()
/*** 创建对象的子列表名称*/public void createSubHead(){if (isSubList()){//创建一个新行,作为子表头。rownum 是当前的行号,表示标题行之后的行Row subRow = sheet.createRow(rownum);int column = 0;//计算子字段的大小int subFieldSize = subFields != null ? subFields.size() : 0;//遍历fields数组。每一项包含一个 Field 对象和一个 Excel 注解对象for (Object[] objects : fields){//获取字段对象Field field = (Field) objects[0];//获取与该字段相关的Excel注解,通常用于指定字段如何映射到Excel表格的列,比如列名、颜色等。Excel attr = (Excel) objects[1];//检查当前字段是否为 Collection 类型(如 List, Set 等)if (Collection.class.isAssignableFrom(field.getType())){//为子表头行的当前列创建一个单元格Cell cell = subRow.createCell(column);//将 Excel 注解中的 name 值设置为单元格的内容cell.setCellValue(attr.name());//设置单元格样式cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));//如果子字段的数量大于 1,意味着该字段需要跨越多个列,因此需要合并单元格if (subFieldSize > 1){//创建一个单元格范围对象CellRangeAddress cellAddress = new CellRangeAddress(rownum, rownum, column, column + subFieldSize - 1);//将合并区域添加到表格中sheet.addMergedRegion(cellAddress);}//更新列索引,跳过已合并的子字段列column += subFieldSize;}else{Cell cell = subRow.createCell(column++);cell.setCellValue(attr.name());cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));}}rownum++;}}
2.exportExcel(HttpServletResponse response)
/*** 对list数据源将其里面的数据导入到excel表单* * @return 结果*/public void exportExcel(HttpServletResponse response){try{writeSheet();//将数据写入Excel表单//将工作簿写入HttpServletResponse输出流,以便客户端下载wb.write(response.getOutputStream());}catch (Exception e){log.error("导出Excel异常{}", e.getMessage());}finally{IOUtils.closeQuietly(wb);}}/*** 创建写入数据到Sheet*/public void writeSheet(){// 取出一共有多少个sheet.//Math.ceil() 计算总页数,并确保至少有一个工作表(即 Math.max(1, ...))//list.size() 是数据列表的大小,sheetSize 是每个工作表的最大行数。int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize));for (int index = 0; index < sheetNo; index++){// 创建新的工作表createSheet(sheetNo, index);// 产生一行Row row = sheet.createRow(rownum);// 初始化列索引int column = 0;// 写入各个字段的列头名称for (Object[] os : fields){Field field = (Field) os[0]; // 获取字段对象Excel excel = (Excel) os[1]; // 获取Excel注解if (Collection.class.isAssignableFrom(field.getType())){for (Field subField : subFields){// 获取子字段的Excel注解Excel subExcel = subField.getAnnotation(Excel.class);// 创建子字段表头单元格this.createHeadCell(subExcel, row, column++);}}else // 如果字段不是集合类型{// 创建普通字段的表头单元格this.createHeadCell(excel, row, column++);}}//判断type类型是导出if (Type.EXPORT.equals(type)){// 填充数据到Excel行fillExcelData(index, row);// 添加统计行addStatisticsRow();}}}/*** 填充excel数据* * @param index 序号* @param row 单元格行*///@SuppressWarnings 注解用于抑制编译器产生的警告信息//unchecked 消除 "类型不安全" 警告@SuppressWarnings("unchecked")public void fillExcelData(int index, Row row){//根据当前工作表的索引和每个工作表的最大行数,确定当前工作表需要填充的数据范围。int startNo = index * sheetSize;int endNo = Math.min(startNo + sheetSize, list.size());int currentRowNum = rownum + 1; // 从标题行后开始//遍历数据列表并填充 Excel 行for (int i = startNo; i < endNo; i++){row = sheet.createRow(currentRowNum);T vo = (T) list.get(i);int column = 0;//获取子列表最大数int maxSubListSize = getCurrentMaxSubListSize(vo);for (Object[] os : fields){Field field = (Field) os[0];Excel excel = (Excel) os[1];if (Collection.class.isAssignableFrom(field.getType())){try{//如果字段是集合类型(如 List 或 Set),首先通过 getTargetValue() 方法获取该字段的值(即集合对象)Collection<?> subList = (Collection<?>) getTargetValue(vo, field, excel);//如果集合不为空,遍历集合中的每个元素 subVo(即子对象),并为每个子对象创建一个子行 subRowif (subList != null && !subList.isEmpty()){int subIndex = 0;for (Object subVo : subList){Row subRow = sheet.getRow(currentRowNum + subIndex);if (subRow == null){subRow = sheet.createRow(currentRowNum + subIndex);}//在子行中,遍历子字段 subFields,为每个子字段创建一个单元格。int subColumn = column;for (Field subField : subFields){Excel subExcel = subField.getAnnotation(Excel.class);//调用 addCell() 方法填充每个子字段的值到 Excel 单元格中addCell(subExcel, subRow, (T) subVo, subField, subColumn++);}subIndex++;}//column 变量在处理完一个集合字段后增加,跳到下一个字段的位置column += subFields.size();}}catch (Exception e){log.error("填充集合数据失败", e);}}else{// 创建单元格并设置值addCell(excel, row, vo, field, column);if (maxSubListSize > 1 && excel.needMerge()){//如果该字段需要合并单元格(excel.needMerge() 返回 true),并且该字段有多个子集合项(maxSubListSize > 1),// 则通过 sheet.addMergedRegion() 方法合并相应的单元格。sheet.addMergedRegion(new CellRangeAddress(currentRowNum, currentRowNum + maxSubListSize - 1, column, column));}column++;}}//更新当前行号currentRowNum += maxSubListSize;}}
总结:以上是若依导出功能的关键代码,理解的可能不太全面,仅供个人参考
相关文章:
若依-导出后端解析
针对若依框架微服务版本学习 若依导入导出功能的具体使用详见:后台手册 | RuoYi 1.导出逻辑: 导出文件的逻辑是先创建一个临时文件,等待前端请求下载结束后马上删除这个临时文件。但是有些下载插件,例如迅雷(他们是二…...
华为OD机试九日集训第1期 - 按算法分类,由易到难,循序渐进,提升编程能力和解题技巧,从而提高机试通过率(Python/JS/C/C++)
目录 一、适合人群二、本期训练时间三、如何参加四、数据结构与算法大纲五、华为OD九日集训第1期第1天、逻辑分析第2天、数组第3天、双指针第4天、map与list第5天、队列第6天、栈第7天、滑动窗口第8天、二叉树第9天、矩阵 六、国内直接使用满血ChatGPT4o、o1、o3-mini-high、Cl…...
Python 机器学习小项目:手写数字识别(MNIST 数据集)
本项目将使用 scikit-learn 库,基于 支持向量机(SVM) 模型来构建一个手写数字识别系统。数据集选用 MNIST,其中包含 0-9 的手写数字图像,每张图片是 88 像素的灰度图。 项目步骤 安装必要的库加载数据集数据预处理划…...
STM32中输入/输出有无默认电平
结论: 输入从严格意义上来讲没有默认高低电平之说 推挽输出模式: 在推挽输出模式下,STM32的GPIO引脚可以输出高电平和低电平。如果没有通过软件明确设置引脚的电平状态,STM32的某些型号(或特定情况下)可能会…...
webshell一些上传心得
我们以upload-labs为基础 一、前端拦截: 如第一关 工作方式: 直接在前端拦截 绕过方式: 因为没有限制后端,所有可以用bs 绕过前端修改格式即可 将需要上传的php文件改成jpg格式 使用burp suite 拦截上传后,使用re…...
ROS实践(二)构建Gazebo机器人模型文件urdf
目录 一、基础语法 1. urdf文件组成 2. robot根标签 3. link 和 joint标签 4. sensor标签 二、 实验:使用launch文件启动rviz查看机器人模型 1. 编写机器人模型的urdf文件。 2. 编写launch文件。 3. 运行launch,查看效果。 URDF(Unifi…...
Linux 入门:常用命令速查手册
目录 一.指令 1.pwd(显示所在路径) 2.ls(列出所有子目录与文件) 3.touch(创建文件) 4.mkdir(创建目录) 5.cd(改变所处位置) 6.rm(删除&…...
kali linux web扫描工具
Kali Linux是一款专为网络安全领域而打造的操作系统,提供了众多优秀的安全工具,其中就包括了强大的web扫描工具。Web扫描是网络安全检测的一个重要环节,它可以帮助安全专家检测网站的漏洞,提升网站的安全性。 Kali Linux中集成了…...
2路模拟量同步输出卡、任意波形发生器卡—PCIe9100数据采集卡
品牌:阿尔泰科技 型号: PCIe9100、PCIe9101、PXIe9100、PXIe9101 产品系列:任意波形发生器 支持操作系统:XP、Win7、Win8、Win10 简要介绍: 910X 系列是阿尔泰科技公司推出的 PCIe、PXIe 总线的任意波形发生器&…...
Facebook 隐私保护技术的发展与未来趋势
Facebook 隐私保护技术的发展与未来趋势 在这个数字化时代,个人隐私保护已成为全球关注的焦点。Facebook,作为全球最大的社交网络平台之一,其在隐私保护技术的发展上扮演着重要角色。本文将探讨 Facebook 在隐私保护技术方面的进展ÿ…...
Android TCP封装工具类
TCP通信的封装,我们可以从以下几个方面进行改进: 线程池优化:使用更高效的线程池配置,避免频繁创建和销毁线程。 连接重试机制:在网络不稳定时,自动重试连接。 心跳机制:保持长连接ÿ…...
Python基于Django的医用耗材网上申领系统【附源码、文档说明】
博主介绍:✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇&…...
Java虚拟机之垃圾收集(一)
目录 一、如何判定对象“生死”? 1. 引用计数算法(理论参考) 2. 可达性分析算法(JVM 实际使用) 3. 对象的“缓刑”机制 二、引用类型与回收策略 三、何时触发垃圾回收? 1. 分代回收策略 2. 手动触发…...
【mysql】mysql数据库,权限授予,账号设置
创建一个可用的数据库,名字为gitea,支持中文utf8mb4 CREATE DATABASE gitea CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 设置东八区时区 SET GLOBAL time_zone 08:00; FLUSH PRIVILEGES; # 查询当前时间 SELECT NOW();给【普通用户】授予查…...
Html5学习教程,从入门到精通, HTML5超链接应用的详细语法知识点和案例代码(18)
HTML5超链接应用的详细语法知识点和案例代码 超链接(Hyperlink),也称为跃点链接,是互联网和文档编辑中的一种重要概念。 超链接的定义 超链接是指从一个网页指向一个目标的连接关系,这个目标可以是另一个网页&#…...
⭐LeetCode(数学分类) 48. 旋转图像——优美的数学法转圈(原地修改)⭐
⭐LeetCode(数学分类) 48. 旋转图像——优美的数学法转圈(原地修改)⭐ 示例 1: 输入:root [5,3,6,2,4,null,8,1,null,null,null,7,9] 输出:[1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9] 示例 2: 输入࿱…...
深度学习PyTorch之13种模型精度评估公式及调用方法
深度学习pytorch之22种损失函数数学公式和代码定义 深度学习pytorch之19种优化算法(optimizer)解析 深度学习pytorch之4种归一化方法(Normalization)原理公式解析和参数使用 深度学习pytorch之简单方法自定义9类卷积即插即用 实时…...
tomcat单机多实例部署
一、部署方法 多实例可以运行多个不同的应用,也可以运行相同的应用,类似于虚拟主机,但是他可以做负载均衡。 方式一: 把tomcat的主目录挨个复制,然后把每台主机的端口给改掉就行了。 优点是最简单最直接,…...
Java开发者如何接入并使用DeepSeek
目录 一、准备工作 二、添加DeepSeek SDK依赖 三、初始化DeepSeek客户端 四、数据上传与查询 五、数据处理与分析 六、实际应用案例 七、总结 【博主推荐】:最近发现了一个超棒的人工智能学习网站,内容通俗易懂,风格风趣幽默ÿ…...
win10电脑鼠标速度突然变的很慢?
电脑鼠标突然变很慢,杀毒检测后没问题,鼠标设置也没变,最后发现可能是误触鼠标的“DPI”调节键。 DPI调节键在鼠标滚轮下方,再次点击即可恢复正常鼠标速度。 如果有和-的按键,速度变快,-速度变慢。 图源&…...
第四次CCF-CSP认证(含C++源码)
第四次CCF-CSP认证 第一道(easy)思路及AC代码 第二道(easy)思路及AC代码遇到的问题 第三道(mid)思路及AC代码 第一道(easy) 题目链接 思路及AC代码 这题就是将这个矩阵旋转之后输出…...
Netty基础—1.网络编程基础一
大纲 1.什么是OSI开放系统互连 2.OSI七层模型各层的作用 3.TCP/IP协议的简介 4.TCP和UDP的简介 5.TCP连接的三次握手 6.TCP连接的四次挥手 7.TCP/IP中的数据包 8.TCP通过确认应答与序列号提高可靠性 9.HTTP请求的传输过程 10.HTTP协议报文结构 11.Socket、短连接、长…...
【理想解法学习笔记】
目录 理想解法原理简介算法步骤属性值规范化方法代码示例 理想解法 原理简介 TOPSIS(Technique for Order Preference by Simi larity to IdealSolution)法是一种逼近理想解的排序方法。其基本的处理思路是:首先建立初始化决策矩阵,而后基于规范化后的初…...
98.在 Vue3 中使用 OpenLayers 根据 Resolution 的不同显示不同的地图
在 Vue3 中使用 OpenLayers 根据 Resolution 的不同显示不同的地图 前言 在 Web GIS(地理信息系统)应用开发中,地图的 Resolution(分辨率)是一个重要的概念。不同的 Resolution 适用于不同的地图层级,有时…...
Docker 部署 Vaultwarden
一、前言 1. 官网 1.1 Vaultwarden https://github.com/dani-garcia/vaultwarden https://github.com/wcjxixi/Vaultwarden-Wiki-Chn https://hub.docker.com/r/vaultwarden/server https://rs.ppgg.in/ # Vaultwarden Wiki 中文版 https://geekdaxue.co/read/Vaultward…...
Smart contract -- 自毁合约
在区块链开发中,Solidity 语言提供了强大的功能,其中自毁合约是一个独特且重要的特性。今天,就让我们深入探讨一下 Solidity 中的自毁合约,以及如何使用 selfdestruct 函数。 注意:使用继承时请确保代码的正确性&#…...
unity学习64,第3个小游戏:一个2D跑酷游戏
目录 学习参考 素材资源导入 1 创建项目 1.1 创建1个2D项目 1.2 导入素材 2 背景图bg 2.0 bg素材 2.1 创建背景 2.2 修改素材,且修改摄像机等 2.2.1 修改导入的原始prefab素材 2.2.2 对应调整摄像机 2.2.3 弄好背景 2.3 背景相关脚本实现 2.3.1 错误…...
Python Flask 在网页应用程序中处理错误和异常
Python Flask 在网页应用程序中处理错误和异常 Python Flask 在网页应用程序中处理错误和异常 Python Flask 在网页应用程序中处理错误和异常 在我们所有的代码示例中,我们没有注意如何处理用户在浏览器中输入错误的URL或向我们的应用程序发送错误的参数集的情况。…...
模板方法模式的C++实现示例
核心思想 模板方法设计模式是一种行为设计模式,它定义了一个算法的框架,并将某些步骤的具体实现延迟到子类中。通过这种方式,模板方法模式允许子类在不改变算法结构的情况下重新定义算法的某些步骤。 模板方法模式的核心在于: …...
水下机器人推进器PID参数整定与MATLAB仿真
水下机器人推进器PID参数整定与MATLAB仿真 1. PID控制原理 目标:通过调节比例(P)、积分(I)、微分(D)参数,使推进器输出力快速稳定跟踪期望值。传递函数(示例):推进器动力学模型可简化为: [ G(s) = \frac{K}{\tau s + 1} \cdot e^{-Ts} ] 其中:K为增益,τ为时间常…...
