当前位置: 首页 > news >正文

若依 ruoyi poi Excel合并行的导入

本文仅针对文字相关的合并做了处理 ,图片合并及保存需要另做处理!!

目标:Excel合并行内容的导入

结果:

1. ExcelUtil.java 类,新增方法:判断是否是合并行

    /*** 新增 合并行相关代码:判断是否是合并行** @param sheet* @param row* @param column* @return*/private boolean isMergedRow(Sheet sheet, int row, int column) {int sheetMergeCount = sheet.getNumMergedRegions();//当前表格(sheet)中,所有合单元格总数//遍历所有合并单元格for (int i = 0; i < sheetMergeCount; i++) {CellRangeAddress cellAddresses = sheet.getMergedRegion(i);//当前合并单元格对象int firstColumn = cellAddresses.getFirstColumn();//当前合并单元格的首列int lastColumn = cellAddresses.getLastColumn();//当前合并单元格的末列int firstRow = cellAddresses.getFirstRow();//当前合并单元格的首行int lastRow = cellAddresses.getLastRow();//当前合并单元格的末行//指定的行 包含在合并单元格中if (row >= firstRow && row <= lastRow) {//指定的列 包含在合并单元格中if (column >= firstColumn && column <= lastColumn) {return true;}}}return false;}

2. ExcelUtil.java 类,新增方法:获取合并行单元格数据

思路:

POI对于Excel的处理:合并行中 只有首列能获取到值  非首列的其他列数据默认是空。由于是合并行,则可以认为  合并行内所有列的数据  都与合并行的首列数据相同。

所以,以下代码中,判断该列如果包含在合并行内,则该列值默认赋值为 所在合并行的首列值。

 图1 Excel中的2/3/4行内的合并行内容相同:

 POI对Excel处理时,默认获取到的值大概如图2:下面代码则在图2逻辑上 对3/4行的空内容做赋值操作

    /*** 新增 合并行相关代码:获取合并行的数据** @param sheet* @param row* @param column* @return*/private String getMergedRegionValue(Sheet sheet, int row, int column) {// 获得该sheet所有合并单元格数量int sheetMergeCount = sheet.getNumMergedRegions();for (int i = 0; i < sheetMergeCount; i++) {// 获得合并区域CellRangeAddress cellAddresses = sheet.getMergedRegion(i);int firstColumn = cellAddresses.getFirstColumn();int lastColumn = cellAddresses.getLastColumn();int firstRow = cellAddresses.getFirstRow();int lastRow = cellAddresses.getLastRow();/*判断传入的单元格的行号列号是否在合并单元格的范围内,如果在合并单元格的范围内,择返回合并区域的首单元格格值*/if (row >= firstRow && row <= lastRow) {if (column >= firstColumn && column <= lastColumn) {Row firRow = sheet.getRow(firstRow);/* 合并行使用的获取单元格内容方法Cell firCell = firRow.getCell(firstColumn);return getCellValue(firCell);*/return getCellValue(firRow, firstColumn).toString();//改为使用 若依自带获取单元格内容方法}}}// 如果该单元格行号列号不在任何一个合并区域,则返回nullreturn null;}

3. ExcelUtil.java的importExcel()方法中 遍历行内所有单元格内容下,新增: 合并行的判断 和 其内单元格内容获取

// ============== 新增 合并行相关代码块 ================// 判断是否合并行  isMergedRow(sheet, rowNum, column):是合并行,则获取合并行数据 重新赋值给当前列
if (isMergedRow(sheet, i, entry.getKey())) val = getMergedRegionValue(sheet, i, entry.getKey());// ============== 新增 合并行相关代码块 ================

importExcel()方法完整代码

    /*** 对excel表单指定表格索引名转换成list* * @param sheetName 表格索引名* @param titleNum 标题占用行数* @param is 输入流* @return 转换后集合*/public List<T> importExcel(String sheetName, InputStream is, int titleNum) throws Exception{this.type = Type.IMPORT;this.wb = WorkbookFactory.create(is);List<T> list = new ArrayList<T>();// 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheetSheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);if (sheet == null){throw new IOException("文件sheet不存在");}boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook);//获取文档指定sheet中所有图片:图片位置、文件流Map<String, PictureData> pictures;if (isXSSFWorkbook){pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb);}else{pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb);}// 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1int rows = sheet.getLastRowNum();if (rows > 0){// 定义一个map用于存放excel列的序号和field.Map<String, Integer> cellMap = new HashMap<String, Integer>();// 获取表头Row heard = sheet.getRow(titleNum);for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++){Cell cell = heard.getCell(i);if (StringUtils.isNotNull(cell)){String value = this.getCellValue(heard, i).toString();cellMap.put(value, i);}else{cellMap.put(null, i);}}// 有数据时才处理 得到类的所有field.List<Object[]> fields = this.getFields();Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>();for (Object[] objects : fields){Excel attr = (Excel) objects[1];Integer column = cellMap.get(attr.name());if (column != null){fieldsMap.put(column, objects);}}//遍历每一行for (int i = titleNum + 1; i <= rows; i++){// 从第2行开始取数据,默认第一行是表头.Row row = sheet.getRow(i);// 判断当前行是否是空行if (isRowEmpty(row)){continue;}T entity = null;//遍历当前行所有列 逐列字段值获取并赋值给entity对象的各属性for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet()){Object val = this.getCellValue(row, entry.getKey());//获取当前列 默认值// ============== 新增 合并行相关代码块 ================// 判断是否合并行  isMergedRow(sheet, rowNum, column):是合并行,则获取合并行首列数据 重新赋值给当前列if (isMergedRow(sheet, i, entry.getKey())) val = getMergedRegionValue(sheet, i, entry.getKey());// ============== 新增 合并行相关代码块 ================// 如果不存在实例则新建.entity = (entity == null ? clazz.newInstance() : entity);// 从map中得到对应列的field.Field field = (Field) entry.getValue()[0];Excel attr = (Excel) entry.getValue()[1];// 取得类型,并根据对象类型设置值.Class<?> fieldType = field.getType();if (String.class == fieldType){String s = Convert.toStr(val);if (StringUtils.endsWith(s, ".0")){val = StringUtils.substringBefore(s, ".0");}else{String dateFormat = field.getAnnotation(Excel.class).dateFormat();if (StringUtils.isNotEmpty(dateFormat)){val = parseDateToStr(dateFormat, val);}else{val = Convert.toStr(val);}}}else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))){val = Convert.toInt(val);}else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))){val = Convert.toLong(val);}else if (Double.TYPE == fieldType || Double.class == fieldType){val = Convert.toDouble(val);}else if (Float.TYPE == fieldType || Float.class == fieldType){val = Convert.toFloat(val);}else if (BigDecimal.class == fieldType){val = Convert.toBigDecimal(val);}else if (Date.class == fieldType){if (val instanceof String){val = DateUtils.parseDate(val);}else if (val instanceof Double){val = DateUtil.getJavaDate((Double) val);}}else if (Boolean.TYPE == fieldType || Boolean.class == fieldType){val = Convert.toBool(val, false);}if (StringUtils.isNotNull(fieldType)){String propertyName = field.getName();if (StringUtils.isNotEmpty(attr.targetAttr())){propertyName = field.getName() + "." + attr.targetAttr();}if (StringUtils.isNotEmpty(attr.readConverterExp())){val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());}else if (StringUtils.isNotEmpty(attr.dictType())){val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());}else if (!attr.handler().equals(ExcelHandlerAdapter.class)){val = dataFormatHandlerAdapter(val, attr, null);}else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures)){//获取指定行指定列中的图片文件:从获取的文件集合中获取PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey());if (image == null){val = "";}else{byte[] data = image.getData();val = FileUtils.writeImportBytes(data);//图片文件写入到 application.yml文件 ruoyi.profile属性对应的文件目录下}}ReflectUtils.invokeSetter(entity, propertyName, val);}}list.add(entity);}}return list;}

最终,工具类 ExcelUtil.java:

package com.ruoyi.common.utils.poi;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFPicture;
import org.apache.poi.hssf.usermodel.HSSFPictureData;
import org.apache.poi.hssf.usermodel.HSSFShape;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Name;
import org.apache.poi.ss.usermodel.PictureData;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFPicture;
import org.apache.poi.xssf.usermodel.XSSFShape;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.Excel.Type;
import com.ruoyi.common.annotation.Excels;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.exception.UtilException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileTypeUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.file.ImageUtils;
import com.ruoyi.common.utils.reflect.ReflectUtils;/*** Excel相关处理* * @author ruoyi*/
public class ExcelUtil<T>
{private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);public static final String FORMULA_REGEX_STR = "=|-|\\+|@";public static final String[] FORMULA_STR = { "=", "-", "+", "@" };/*** 用于dictType属性数据存储,避免重复查缓存*/public Map<String, String> sysDictMap = new HashMap<String, String>();/*** Excel sheet最大行数,默认65536*/public static final int sheetSize = 65536;/*** 工作表名称*/private String sheetName;/*** 导出类型(EXPORT:导出数据;IMPORT:导入模板)*/private Type type;/*** 工作薄对象*/private Workbook wb;/*** 工作表对象*/private Sheet sheet;/*** 样式列表*/private Map<String, CellStyle> styles;/*** 导入导出数据列表*/private List<T> list;/*** 注解列表*/private List<Object[]> fields;/*** 当前行号*/private int rownum;/*** 标题*/private String title;/*** 最大高度*/private short maxHeight;/*** 合并后最后行数*/private int subMergedLastRowNum = 0;/*** 合并后开始行数*/private int subMergedFirstRowNum = 1;/*** 对象的子列表方法*/private Method subMethod;/*** 对象的子列表属性*/private List<Field> subFields;/*** 统计列表*/private Map<Integer, Double> statistics = new HashMap<Integer, Double>();/*** 数字格式*/private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");/*** 实体对象*/public Class<T> clazz;/*** 需要排除列属性*/public String[] excludeFields;public ExcelUtil(Class<T> clazz){this.clazz = clazz;}/*** 隐藏Excel中列属性** @param fields 列属性名 示例[单个"name"/多个"id","name"]* @throws Exception*/public void hideColumn(String... fields){this.excludeFields = fields;}public void init(List<T> list, String sheetName, String title, Type type){if (list == null){list = new ArrayList<T>();}this.list = list;this.sheetName = sheetName;this.type = type;this.title = title;createExcelField();createWorkbook();createTitle();createSubHead();}/*** 创建excel第一行标题*/public void createTitle(){if (StringUtils.isNotEmpty(title)){subMergedFirstRowNum++;subMergedLastRowNum++;int titleLastCol = this.fields.size() - 1;if (isSubList()){titleLastCol = titleLastCol + subFields.size() - 1;}Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);titleRow.setHeightInPoints(30);Cell titleCell = titleRow.createCell(0);titleCell.setCellStyle(styles.get("title"));titleCell.setCellValue(title);sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol));}}/*** 创建对象的子列表名称*/public void createSubHead(){if (isSubList()){subMergedFirstRowNum++;subMergedLastRowNum++;Row subRow = sheet.createRow(rownum);int excelNum = 0;for (Object[] objects : fields){Excel attr = (Excel) objects[1];Cell headCell1 = subRow.createCell(excelNum);headCell1.setCellValue(attr.name());headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));excelNum++;}int headFirstRow = excelNum - 1;int headLastRow = headFirstRow + subFields.size() - 1;if (headLastRow > headFirstRow){sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow));}rownum++;}}/*** 对excel表单默认第一个索引名转换成list* * @param is 输入流* @return 转换后集合*/public List<T> importExcel(InputStream is){List<T> list = null;try{list = importExcel(is, 0);}catch (Exception e){log.error("导入Excel异常{}", e.getMessage());throw new UtilException(e.getMessage());}finally{IOUtils.closeQuietly(is);}return list;}/*** 对excel表单默认第一个索引名转换成list* * @param is 输入流* @param titleNum 标题占用行数* @return 转换后集合*/public List<T> importExcel(InputStream is, int titleNum) throws Exception{return importExcel(StringUtils.EMPTY, is, titleNum);}/*** 对excel表单指定表格索引名转换成list* * @param sheetName 表格索引名* @param titleNum 标题占用行数* @param is 输入流* @return 转换后集合*/public List<T> importExcel(String sheetName, InputStream is, int titleNum) throws Exception{this.type = Type.IMPORT;this.wb = WorkbookFactory.create(is);List<T> list = new ArrayList<T>();// 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheetSheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);if (sheet == null){throw new IOException("文件sheet不存在");}boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook);//获取文档指定sheet中所有图片:图片位置、文件流Map<String, PictureData> pictures;if (isXSSFWorkbook){pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb);}else{pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb);}// 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1int rows = sheet.getLastRowNum();if (rows > 0){// 定义一个map用于存放excel列的序号和field.Map<String, Integer> cellMap = new HashMap<String, Integer>();// 获取表头Row heard = sheet.getRow(titleNum);for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++){Cell cell = heard.getCell(i);if (StringUtils.isNotNull(cell)){String value = this.getCellValue(heard, i).toString();cellMap.put(value, i);}else{cellMap.put(null, i);}}// 有数据时才处理 得到类的所有field.List<Object[]> fields = this.getFields();Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>();for (Object[] objects : fields){Excel attr = (Excel) objects[1];Integer column = cellMap.get(attr.name());if (column != null){fieldsMap.put(column, objects);}}for (int i = titleNum + 1; i <= rows; i++){// 从第2行开始取数据,默认第一行是表头.Row row = sheet.getRow(i);// 判断当前行是否是空行if (isRowEmpty(row)){continue;}T entity = null;//遍历所有列 逐列字段值获取并赋值给entity对象的各属性for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet()){Object val = this.getCellValue(row, entry.getKey());//获取当前列 默认值// ============== 新增 合并行相关代码块 ================// 判断是否合并行  isMergedRow(sheet, rowNum, column):是合并行,则获取合并行数据 重新赋值给当前列if (isMergedRow(sheet, i, entry.getKey())) val = getMergedRegionValue(sheet, i, entry.getKey());// ============== 新增 合并行相关代码块 ================// 如果不存在实例则新建.entity = (entity == null ? clazz.newInstance() : entity);// 从map中得到对应列的field.Field field = (Field) entry.getValue()[0];Excel attr = (Excel) entry.getValue()[1];// 取得类型,并根据对象类型设置值.Class<?> fieldType = field.getType();if (String.class == fieldType){String s = Convert.toStr(val);if (StringUtils.endsWith(s, ".0")){val = StringUtils.substringBefore(s, ".0");}else{String dateFormat = field.getAnnotation(Excel.class).dateFormat();if (StringUtils.isNotEmpty(dateFormat)){val = parseDateToStr(dateFormat, val);}else{val = Convert.toStr(val);}}}else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))){val = Convert.toInt(val);}else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))){val = Convert.toLong(val);}else if (Double.TYPE == fieldType || Double.class == fieldType){val = Convert.toDouble(val);}else if (Float.TYPE == fieldType || Float.class == fieldType){val = Convert.toFloat(val);}else if (BigDecimal.class == fieldType){val = Convert.toBigDecimal(val);}else if (Date.class == fieldType){if (val instanceof String){val = DateUtils.parseDate(val);}else if (val instanceof Double){val = DateUtil.getJavaDate((Double) val);}}else if (Boolean.TYPE == fieldType || Boolean.class == fieldType){val = Convert.toBool(val, false);}if (StringUtils.isNotNull(fieldType)){String propertyName = field.getName();if (StringUtils.isNotEmpty(attr.targetAttr())){propertyName = field.getName() + "." + attr.targetAttr();}if (StringUtils.isNotEmpty(attr.readConverterExp())){val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());}else if (StringUtils.isNotEmpty(attr.dictType())){val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());}else if (!attr.handler().equals(ExcelHandlerAdapter.class)){val = dataFormatHandlerAdapter(val, attr, null);}else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures)){PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey());if (image == null){val = "";}else{byte[] data = image.getData();val = FileUtils.writeImportBytes(data);//图片文件写入到 application.yml文件 ruoyi.profile属性对应的文件目录下}}ReflectUtils.invokeSetter(entity, propertyName, val);}}list.add(entity);}}return list;}/*** 新增 合并行相关代码:判断是否是合并行** @param sheet* @param row* @param column* @return*/private boolean isMergedRow(Sheet sheet, int row, int column) {int sheetMergeCount = sheet.getNumMergedRegions();for (int i = 0; i < sheetMergeCount; i++) {CellRangeAddress cellAddresses = sheet.getMergedRegion(i);int firstColumn = cellAddresses.getFirstColumn();int lastColumn = cellAddresses.getLastColumn();int firstRow = cellAddresses.getFirstRow();int lastRow = cellAddresses.getLastRow();if (row >= firstRow && row <= lastRow) {if (column >= firstColumn && column <= lastColumn) {return true;}}}return false;}/*** 新增 合并行相关代码:获取合并行的数据** @param sheet* @param row* @param column* @return*/private String getMergedRegionValue(Sheet sheet, int row, int column) {// 获得该sheet所有合并单元格数量int sheetMergeCount = sheet.getNumMergedRegions();for (int i = 0; i < sheetMergeCount; i++) {// 获得合并区域CellRangeAddress cellAddresses = sheet.getMergedRegion(i);int firstColumn = cellAddresses.getFirstColumn();int lastColumn = cellAddresses.getLastColumn();int firstRow = cellAddresses.getFirstRow();int lastRow = cellAddresses.getLastRow();/*判断传入的单元格的行号列号是否在合并单元格的范围内,如果在合并单元格的范围内,择返回合并区域的首单元格格值*/if (row >= firstRow && row <= lastRow) {if (column >= firstColumn && column <= lastColumn) {Row firRow = sheet.getRow(firstRow);/* 合并行使用的获取单元格内容方法Cell firCell = firRow.getCell(firstColumn);return getCellValue(firCell);*/return getCellValue(firRow, firstColumn).toString();//改为使用 若依自带获取单元格内容方法}}}// 如果该单元格行号列号不在任何一个合并区域,则返回nullreturn null;}/*** 对list数据源将其里面的数据导入到excel表单* * @param list 导出数据集合* @param sheetName 工作表的名称* @return 结果*/public AjaxResult exportExcel(List<T> list, String sheetName){return exportExcel(list, sheetName, StringUtils.EMPTY);}/*** 对list数据源将其里面的数据导入到excel表单* * @param list 导出数据集合* @param sheetName 工作表的名称* @param title 标题* @return 结果*/public AjaxResult exportExcel(List<T> list, String sheetName, String title){this.init(list, sheetName, title, Type.EXPORT);return exportExcel();}/*** 对list数据源将其里面的数据导入到excel表单* * @param response 返回数据* @param list 导出数据集合* @param sheetName 工作表的名称* @return 结果*/public void exportExcel(HttpServletResponse response, List<T> list, String sheetName){exportExcel(response, list, sheetName, StringUtils.EMPTY);}/*** 对list数据源将其里面的数据导入到excel表单* * @param response 返回数据* @param list 导出数据集合* @param sheetName 工作表的名称* @param title 标题* @return 结果*/public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title){response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");this.init(list, sheetName, title, Type.EXPORT);exportExcel(response);}/*** 对list数据源将其里面的数据导入到excel表单* * @param sheetName 工作表的名称* @return 结果*/public AjaxResult importTemplateExcel(String sheetName){return importTemplateExcel(sheetName, StringUtils.EMPTY);}/*** 对list数据源将其里面的数据导入到excel表单* * @param sheetName 工作表的名称* @param title 标题* @return 结果*/public AjaxResult importTemplateExcel(String sheetName, String title){this.init(null, sheetName, title, Type.IMPORT);return exportExcel();}/*** 对list数据源将其里面的数据导入到excel表单* * @param sheetName 工作表的名称* @return 结果*/public void importTemplateExcel(HttpServletResponse response, String sheetName){importTemplateExcel(response, sheetName, StringUtils.EMPTY);}/*** 对list数据源将其里面的数据导入到excel表单* * @param sheetName 工作表的名称* @param title 标题* @return 结果*/public void importTemplateExcel(HttpServletResponse response, String sheetName, String title){response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");this.init(null, sheetName, title, Type.IMPORT);exportExcel(response);}/*** 对list数据源将其里面的数据导入到excel表单* * @return 结果*/public void exportExcel(HttpServletResponse response){try{writeSheet();wb.write(response.getOutputStream());}catch (Exception e){log.error("导出Excel异常{}", e.getMessage());}finally{IOUtils.closeQuietly(wb);}}/*** 对list数据源将其里面的数据导入到excel表单* * @return 结果*/public AjaxResult exportExcel(){OutputStream out = null;try{writeSheet();String filename = encodingFilename(sheetName);out = new FileOutputStream(getAbsoluteFile(filename));wb.write(out);return AjaxResult.success(filename);}catch (Exception e){log.error("导出Excel异常{}", e.getMessage());throw new UtilException("导出Excel失败,请联系网站管理员!");}finally{IOUtils.closeQuietly(wb);IOUtils.closeQuietly(out);}}/*** 创建写入数据到Sheet*/public void writeSheet(){// 取出一共有多少个sheet.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];if (Collection.class.isAssignableFrom(field.getType())){for (Field subField : subFields){Excel subExcel = subField.getAnnotation(Excel.class);this.createHeadCell(subExcel, row, column++);}}else{this.createHeadCell(excel, row, column++);}}if (Type.EXPORT.equals(type)){fillExcelData(index, row);addStatisticsRow();}}}/*** 填充excel数据* * @param index 序号* @param row 单元格行*/@SuppressWarnings("unchecked")public void fillExcelData(int index, Row row){int startNo = index * sheetSize;int endNo = Math.min(startNo + sheetSize, list.size());int rowNo = (1 + rownum) - startNo;for (int i = startNo; i < endNo; i++){rowNo = isSubList() ? (i > 1 ? rowNo + 1 : rowNo + i) : i + 1 + rownum - startNo;row = sheet.createRow(rowNo);// 得到导出对象.T vo = (T) list.get(i);Collection<?> subList = null;if (isSubList()){if (isSubListValue(vo)){subList = getListCellValue(vo);subMergedLastRowNum = subMergedLastRowNum + subList.size();}else{subMergedFirstRowNum++;subMergedLastRowNum++;}}int column = 0;for (Object[] os : fields){Field field = (Field) os[0];Excel excel = (Excel) os[1];if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList)){boolean subFirst = false;for (Object obj : subList){if (subFirst){rowNo++;row = sheet.createRow(rowNo);}List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class);int subIndex = 0;for (Field subField : subFields){if (subField.isAnnotationPresent(Excel.class)){subField.setAccessible(true);Excel attr = subField.getAnnotation(Excel.class);this.addCell(attr, row, (T) obj, subField, column + subIndex);}subIndex++;}subFirst = true;}this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size();}else{this.addCell(excel, row, vo, field, column++);}}}}/*** 创建表格样式* * @param wb 工作薄对象* @return 样式列表*/private Map<String, CellStyle> createStyles(Workbook wb){// 写入各条记录,每条记录对应excel表中的一行Map<String, CellStyle> styles = new HashMap<String, CellStyle>();CellStyle style = wb.createCellStyle();style.setAlignment(HorizontalAlignment.CENTER);style.setVerticalAlignment(VerticalAlignment.CENTER);Font titleFont = wb.createFont();titleFont.setFontName("Arial");titleFont.setFontHeightInPoints((short) 16);titleFont.setBold(true);style.setFont(titleFont);styles.put("title", style);style = wb.createCellStyle();style.setAlignment(HorizontalAlignment.CENTER);style.setVerticalAlignment(VerticalAlignment.CENTER);style.setBorderRight(BorderStyle.THIN);style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());style.setBorderLeft(BorderStyle.THIN);style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());style.setBorderTop(BorderStyle.THIN);style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());style.setBorderBottom(BorderStyle.THIN);style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());Font dataFont = wb.createFont();dataFont.setFontName("Arial");dataFont.setFontHeightInPoints((short) 10);style.setFont(dataFont);styles.put("data", style);style = wb.createCellStyle();style.setAlignment(HorizontalAlignment.CENTER);style.setVerticalAlignment(VerticalAlignment.CENTER);Font totalFont = wb.createFont();totalFont.setFontName("Arial");totalFont.setFontHeightInPoints((short) 10);style.setFont(totalFont);styles.put("total", style);styles.putAll(annotationHeaderStyles(wb, styles));styles.putAll(annotationDataStyles(wb));return styles;}/*** 根据Excel注解创建表格头样式* * @param wb 工作薄对象* @return 自定义样式列表*/private Map<String, CellStyle> annotationHeaderStyles(Workbook wb, Map<String, CellStyle> styles){Map<String, CellStyle> headerStyles = new HashMap<String, CellStyle>();for (Object[] os : fields){Excel excel = (Excel) os[1];String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor());if (!headerStyles.containsKey(key)){CellStyle style = wb.createCellStyle();style.cloneStyleFrom(styles.get("data"));style.setAlignment(HorizontalAlignment.CENTER);style.setVerticalAlignment(VerticalAlignment.CENTER);style.setFillForegroundColor(excel.headerBackgroundColor().index);style.setFillPattern(FillPatternType.SOLID_FOREGROUND);Font headerFont = wb.createFont();headerFont.setFontName("Arial");headerFont.setFontHeightInPoints((short) 10);headerFont.setBold(true);headerFont.setColor(excel.headerColor().index);style.setFont(headerFont);headerStyles.put(key, style);}}return headerStyles;}/*** 根据Excel注解创建表格列样式* * @param wb 工作薄对象* @return 自定义样式列表*/private Map<String, CellStyle> annotationDataStyles(Workbook wb){Map<String, CellStyle> styles = new HashMap<String, CellStyle>();for (Object[] os : fields){Excel excel = (Excel) os[1];String key = StringUtils.format("data_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor());if (!styles.containsKey(key)){CellStyle style = wb.createCellStyle();style.setAlignment(excel.align());style.setVerticalAlignment(VerticalAlignment.CENTER);style.setBorderRight(BorderStyle.THIN);style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());style.setBorderLeft(BorderStyle.THIN);style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());style.setBorderTop(BorderStyle.THIN);style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());style.setBorderBottom(BorderStyle.THIN);style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());style.setFillPattern(FillPatternType.SOLID_FOREGROUND);style.setFillForegroundColor(excel.backgroundColor().getIndex());Font dataFont = wb.createFont();dataFont.setFontName("Arial");dataFont.setFontHeightInPoints((short) 10);dataFont.setColor(excel.color().index);style.setFont(dataFont);styles.put(key, style);}}return styles;}/*** 创建单元格*/public Cell createHeadCell(Excel attr, Row row, int column){// 创建列Cell cell = row.createCell(column);// 写入列信息cell.setCellValue(attr.name());setDataValidation(attr, row, column);cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));if (isSubList()){// 填充默认样式,防止合并单元格样式失效sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor())));if (attr.needMerge()){sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column));}}return cell;}/*** 设置单元格信息* * @param value 单元格值* @param attr 注解相关* @param cell 单元格信息*/public void setCellVo(Object value, Excel attr, Cell cell){if (ColumnType.STRING == attr.cellType()){String cellValue = Convert.toStr(value);// 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。if (StringUtils.startsWithAny(cellValue, FORMULA_STR)){cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0");}if (value instanceof Collection && StringUtils.equals("[]", cellValue)){cellValue = StringUtils.EMPTY;}cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix());}else if (ColumnType.NUMERIC == attr.cellType()){if (StringUtils.isNotNull(value)){cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value));}}else if (ColumnType.IMAGE == attr.cellType()){ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1);String imagePath = Convert.toStr(value);if (StringUtils.isNotEmpty(imagePath)){byte[] data = ImageUtils.getImage(imagePath);getDrawingPatriarch(cell.getSheet()).createPicture(anchor,cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));}}}/*** 获取画布*/public static Drawing<?> getDrawingPatriarch(Sheet sheet){if (sheet.getDrawingPatriarch() == null){sheet.createDrawingPatriarch();}return sheet.getDrawingPatriarch();}/*** 获取图片类型,设置图片插入类型*/public int getImageType(byte[] value){String type = FileTypeUtils.getFileExtendName(value);if ("JPG".equalsIgnoreCase(type)){return Workbook.PICTURE_TYPE_JPEG;}else if ("PNG".equalsIgnoreCase(type)){return Workbook.PICTURE_TYPE_PNG;}return Workbook.PICTURE_TYPE_JPEG;}/*** 创建表格样式*/public void setDataValidation(Excel attr, Row row, int column){if (attr.name().indexOf("注:") >= 0){sheet.setColumnWidth(column, 6000);}else{// 设置列宽sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));}if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0){if (attr.combo().length > 15 || StringUtils.join(attr.combo()).length() > 255){// 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到setXSSFValidationWithHidden(sheet, attr.combo(), attr.prompt(), 1, 100, column, column);}else{// 提示信息或只能选择不能输入的列内容.setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column);}}}/*** 添加单元格*/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);if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()){CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column);sheet.addMergedRegion(cellAddress);}cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor())));// 用于读取对象中的属性Object value = getTargetValue(vo, field, attr);String dateFormat = attr.dateFormat();String readConverterExp = attr.readConverterExp();String separator = attr.separator();String dictType = attr.dictType();if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)){cell.setCellValue(parseDateToStr(dateFormat, 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)){if (!sysDictMap.containsKey(dictType + value)){String lable = convertDictByExp(Convert.toStr(value), dictType, separator);sysDictMap.put(dictType + value, lable);}cell.setCellValue(sysDictMap.get(dictType + value));}else if (value instanceof BigDecimal && -1 != attr.scale()){cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue());}else if (!attr.handler().equals(ExcelHandlerAdapter.class)){cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell));}else{// 设置列类型setCellVo(value, attr, cell);}addStatisticsData(column, Convert.toStr(value), attr);}}catch (Exception e){log.error("导出Excel失败{}", e);}return cell;}/*** 设置 POI XSSFSheet 单元格提示或选择框* * @param sheet 表单* @param textlist 下拉框显示的内容* @param promptContent 提示内容* @param firstRow 开始行* @param endRow 结束行* @param firstCol 开始列* @param endCol 结束列*/public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow,int firstCol, int endCol){DataValidationHelper helper = sheet.getDataValidationHelper();DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1");CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);DataValidation dataValidation = helper.createValidation(constraint, regions);if (StringUtils.isNotEmpty(promptContent)){// 如果设置了提示信息则鼠标放上去提示dataValidation.createPromptBox("", promptContent);dataValidation.setShowPromptBox(true);}// 处理Excel兼容性问题if (dataValidation instanceof XSSFDataValidation){dataValidation.setSuppressDropDownArrow(true);dataValidation.setShowErrorBox(true);}else{dataValidation.setSuppressDropDownArrow(false);}sheet.addValidationData(dataValidation);}/*** 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框).* * @param sheet 要设置的sheet.* @param textlist 下拉框显示的内容* @param promptContent 提示内容* @param firstRow 开始行* @param endRow 结束行* @param firstCol 开始列* @param endCol 结束列*/public void setXSSFValidationWithHidden(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol){String hideSheetName = "combo_" + firstCol + "_" + endCol;Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据for (int i = 0; i < textlist.length; i++){hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]);}// 创建名称,可被其他单元格引用Name name = wb.createName();name.setNameName(hideSheetName + "_data");name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length);DataValidationHelper helper = sheet.getDataValidationHelper();// 加载下拉列表内容DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data");// 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);// 数据有效性对象DataValidation dataValidation = helper.createValidation(constraint, regions);if (StringUtils.isNotEmpty(promptContent)){// 如果设置了提示信息则鼠标放上去提示dataValidation.createPromptBox("", promptContent);dataValidation.setShowPromptBox(true);}// 处理Excel兼容性问题if (dataValidation instanceof XSSFDataValidation){dataValidation.setSuppressDropDownArrow(true);dataValidation.setShowErrorBox(true);}else{dataValidation.setSuppressDropDownArrow(false);}sheet.addValidationData(dataValidation);// 设置hiddenSheet隐藏wb.setSheetHidden(wb.getSheetIndex(hideSheet), true);}/*** 解析导出值 0=男,1=女,2=未知* * @param propertyValue 参数值* @param converterExp 翻译注解* @param separator 分隔符* @return 解析后值*/public static String convertByExp(String propertyValue, String converterExp, String separator){StringBuilder propertyString = new StringBuilder();String[] convertSource = converterExp.split(",");for (String item : convertSource){String[] itemArray = item.split("=");if (StringUtils.containsAny(propertyValue, separator)){for (String value : propertyValue.split(separator)){if (itemArray[0].equals(value)){propertyString.append(itemArray[1] + separator);break;}}}else{if (itemArray[0].equals(propertyValue)){return itemArray[1];}}}return StringUtils.stripEnd(propertyString.toString(), separator);}/*** 反向解析值 男=0,女=1,未知=2* * @param propertyValue 参数值* @param converterExp 翻译注解* @param separator 分隔符* @return 解析后值*/public static String reverseByExp(String propertyValue, String converterExp, String separator){StringBuilder propertyString = new StringBuilder();String[] convertSource = converterExp.split(",");for (String item : convertSource){String[] itemArray = item.split("=");if (StringUtils.containsAny(propertyValue, separator)){for (String value : propertyValue.split(separator)){if (itemArray[1].equals(value)){propertyString.append(itemArray[0] + separator);break;}}}else{if (itemArray[1].equals(propertyValue)){return itemArray[0];}}}return StringUtils.stripEnd(propertyString.toString(), separator);}/*** 解析字典值* * @param dictValue 字典值* @param dictType 字典类型* @param separator 分隔符* @return 字典标签*/public static String convertDictByExp(String dictValue, String dictType, String separator){return DictUtils.getDictLabel(dictType, dictValue, separator);}/*** 反向解析值字典值* * @param dictLabel 字典标签* @param dictType 字典类型* @param separator 分隔符* @return 字典值*/public static String reverseDictByExp(String dictLabel, String dictType, String separator){return DictUtils.getDictValue(dictType, dictLabel, separator);}/*** 数据处理器* * @param value 数据值* @param excel 数据注解* @return*/public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell){try{Object instance = excel.handler().newInstance();Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class });value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb);}catch (Exception e){log.error("不能格式化数据 " + excel.handler(), e.getMessage());}return Convert.toStr(value);}/*** 合计统计信息*/private void addStatisticsData(Integer index, String text, Excel entity){if (entity != null && entity.isStatistics()){Double temp = 0D;if (!statistics.containsKey(index)){statistics.put(index, temp);}try{temp = Double.valueOf(text);}catch (NumberFormatException e){}statistics.put(index, statistics.get(index) + temp);}}/*** 创建统计行*/public void addStatisticsRow(){if (statistics.size() > 0){Row row = sheet.createRow(sheet.getLastRowNum() + 1);Set<Integer> keys = statistics.keySet();Cell cell = row.createCell(0);cell.setCellStyle(styles.get("total"));cell.setCellValue("合计");for (Integer key : keys){cell = row.createCell(key);cell.setCellStyle(styles.get("total"));cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key)));}statistics.clear();}}/*** 编码文件名*/public String encodingFilename(String filename){filename = UUID.randomUUID() + "_" + filename + ".xlsx";return filename;}/*** 获取下载路径* * @param filename 文件名称*/public String getAbsoluteFile(String filename){String downloadPath = RuoYiConfig.getDownloadPath() + filename;File desc = new File(downloadPath);if (!desc.getParentFile().exists()){desc.getParentFile().mkdirs();}return downloadPath;}/*** 获取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);if (StringUtils.isNotEmpty(excel.targetAttr())){String target = excel.targetAttr();if (target.contains(".")){String[] targets = target.split("[.]");for (String name : targets){o = getValue(o, name);}}else{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;}/*** 得到所有定义字段*/private void createExcelField(){this.fields = getFields();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()));for (Field field : tempFields){if (!ArrayUtils.contains(this.excludeFields, field.getName())){// 单注解if (field.isAnnotationPresent(Excel.class)){Excel attr = field.getAnnotation(Excel.class);if (attr != null && (attr.type() == Type.ALL || attr.type() == type)){field.setAccessible(true);fields.add(new Object[] { field, attr });}if (Collection.class.isAssignableFrom(field.getType())){subMethod = getSubMethod(field.getName(), clazz);ParameterizedType pt = (ParameterizedType) field.getGenericType();Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];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 (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr())&& (attr != null && (attr.type() == Type.ALL || attr.type() == type))){field.setAccessible(true);fields.add(new Object[] { field, attr });}}}}}return fields;}/*** 根据注解获取最大行高*/public short getRowHeight(){double maxHeight = 0;for (Object[] os : this.fields){Excel excel = (Excel) os[1];maxHeight = Math.max(maxHeight, excel.height());}return (short) (maxHeight * 20);}/*** 创建一个工作簿*/public void createWorkbook(){this.wb = new SXSSFWorkbook(500);this.sheet = wb.createSheet();wb.setSheetName(0, sheetName);this.styles = createStyles(wb);}/*** 创建工作表* * @param sheetNo sheet数量* @param index 序号*/public void createSheet(int sheetNo, int index){// 设置工作表的名称.if (sheetNo > 1 && index > 0){this.sheet = wb.createSheet();this.createTitle();wb.setSheetName(index, sheetName + index);}}/*** 获取单元格值* * @param row 获取的行* @param column 获取单元格列号* @return 单元格值*/public Object getCellValue(Row row, int column){if (row == null){return row;}Object val = "";try{Cell cell = row.getCell(column);if (StringUtils.isNotNull(cell)){if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA){val = cell.getNumericCellValue();if (DateUtil.isCellDateFormatted(cell)){val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换}else{if ((Double) val % 1 != 0){val = new BigDecimal(val.toString());}else{val = new DecimalFormat("0").format(val);}}}else if (cell.getCellType() == CellType.STRING){val = cell.getStringCellValue();}else if (cell.getCellType() == CellType.BOOLEAN){val = cell.getBooleanCellValue();}else if (cell.getCellType() == CellType.ERROR){val = cell.getErrorCellValue();}}}catch (Exception e){return val;}return val;}/*** 判断是否是空行* * @param row 判断的行* @return*/private boolean isRowEmpty(Row row){if (row == null){return true;}for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++){Cell cell = row.getCell(i);if (cell != null && cell.getCellType() != CellType.BLANK){return false;}}return true;}/*** 获取Excel2003图片** @param sheet 当前sheet对象* @param workbook 工作簿对象* @return Map key:图片单元格索引(1_1)String,value:图片流PictureData*/public static Map<String, PictureData> getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook){Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();List<HSSFPictureData> pictures = workbook.getAllPictures();if (!pictures.isEmpty()){for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()){HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor();if (shape instanceof HSSFPicture){HSSFPicture pic = (HSSFPicture) shape;int pictureIndex = pic.getPictureIndex() - 1;HSSFPictureData picData = pictures.get(pictureIndex);String picIndex = anchor.getRow1() + "_" + anchor.getCol1();sheetIndexPicMap.put(picIndex, picData);}}return sheetIndexPicMap;}else{return sheetIndexPicMap;}}/*** 获取Excel2007图片** @param sheet 当前sheet对象* @param workbook 工作簿对象* @return Map key:图片单元格索引(1_1)String,value:图片流PictureData*/public static Map<String, PictureData> getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook){Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();for (POIXMLDocumentPart dr : sheet.getRelations()){if (dr instanceof XSSFDrawing){XSSFDrawing drawing = (XSSFDrawing) dr;List<XSSFShape> shapes = drawing.getShapes();for (XSSFShape shape : shapes){if (shape instanceof XSSFPicture){XSSFPicture pic = (XSSFPicture) shape;XSSFClientAnchor anchor = pic.getPreferredSize();CTMarker ctMarker = anchor.getFrom();String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol();sheetIndexPicMap.put(picIndex, pic.getPictureData());}}}}return sheetIndexPicMap;}/*** 格式化不同类型的日期对象* * @param dateFormat 日期格式* @param val 被格式化的日期对象* @return 格式化后的日期字符*/public String parseDateToStr(String dateFormat, Object val){if (val == null){return "";}String str;if (val instanceof Date){str = DateUtils.parseDateToStr(dateFormat, (Date) val);}else if (val instanceof LocalDateTime){str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val));}else if (val instanceof LocalDate){str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val));}else{str = val.toString();}return str;}/*** 是否有对象的子列表*/public boolean isSubList(){return StringUtils.isNotNull(subFields) && subFields.size() > 0;}/*** 是否有对象的子列表,集合不为空*/public boolean isSubListValue(T vo){return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0;}/*** 获取集合的值*/public Collection<?> getListCellValue(Object obj){Object value;try{value = subMethod.invoke(obj, new Object[] {});}catch (Exception e){return new ArrayList<Object>();}return (Collection<?>) value;}/*** 获取对象的子列表方法* * @param name 名称* @param pojoClass 类对象* @return 子列表方法*/public Method getSubMethod(String name, Class<?> pojoClass){StringBuffer getMethodName = new StringBuffer("get");getMethodName.append(name.substring(0, 1).toUpperCase());getMethodName.append(name.substring(1));Method method = null;try{method = pojoClass.getMethod(getMethodName.toString(), new Class[] {});}catch (Exception e){log.error("获取对象异常{}", e.getMessage());}return method;}
}

======================================================

参考自:

https://www.iteye.com/blog/357029540-2438298

下面工具类为该文下的POI处理表格数据的源码:

package com.chinamobile.util;import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;/*** @author liaoyubo* @version 1.0* @date 2019/2/27* @description*/
@Slf4j
public class POIUtils {private final static String XLS = "xls";private final static String XLSX = "xlsx";private static SimpleDateFormat sdf;/*** 读入excel文件,解析后返回** @param file* @throws IOException*/public static List<String[]> readExcel(MultipartFile file) throws IOException {//检查文件checkFile(file);//获得Workbook工作薄对象Workbook workbook = getWorkBook(file);//创建返回对象,把每行中的值作为一个数组,所有行作为一个集合返回List<String[]> list = new ArrayList<>();if (workbook != null) {for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) {//获得当前sheet工作表Sheet sheet = workbook.getSheetAt(sheetNum);if (sheet == null) {continue;}//获得当前sheet的开始行int firstRowNum = sheet.getFirstRowNum();//获得当前sheet的结束行int lastRowNum = sheet.getLastRowNum();//循环除了第一行的所有行list = rowList(sheet,firstRowNum + 1,lastRowNum);}workbook.close();}return list;}public static List<String[]> readExcel(MultipartFile file,int sheetNum,int startRow) throws IOException {//检查文件checkFile(file);//获得Workbook工作薄对象Workbook workbook = getWorkBook(file);//创建返回对象,把每行中的值作为一个数组,所有行作为一个集合返回List<String[]> list = new ArrayList<>();if (workbook != null) {//获得当前sheet工作表Sheet sheet = workbook.getSheetAt(sheetNum);if (!Optional.ofNullable(sheet).isPresent()) {return null;}//获得当前sheet的结束行int lastRowNum = sheet.getLastRowNum();//循环指定的所有行list = rowList(sheet,startRow,lastRowNum);workbook.close();}return list;}/*** 检查文件是否正确** @param file* @throws IOException*/private static void checkFile(MultipartFile file) throws IOException {//判断文件是否存在if (null == file) {log.error("文件不存在!");throw new FileNotFoundException("文件不存在!");}//获得文件名String fileName = file.getOriginalFilename();//判断文件是否是excel文件if (!fileName.endsWith(XLS) && !fileName.endsWith(XLSX)) {log.error(fileName + "不是excel文件");throw new IOException(fileName + "不是excel文件");}}/*** 对文件解析** @param file* @return*/private static Workbook getWorkBook(MultipartFile file) {//获得文件名String fileName = file.getOriginalFilename();//创建Workbook工作薄对象,表示整个excelWorkbook workbook = null;try {//获取excel文件的io流InputStream is = file.getInputStream();//根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象if (fileName.endsWith(XLS)) {//2003workbook = new HSSFWorkbook(is);} else if (fileName.endsWith(XLSX)) {//2007workbook = new XSSFWorkbook(is);}} catch (IOException e) {log.info(e.getMessage());}return workbook;}/*** 判断是否是合并行** @param sheet* @param row* @param column* @return*/private static boolean isMergedRow(Sheet sheet, int row, int column) {int sheetMergeCount = sheet.getNumMergedRegions();for (int i = 0; i < sheetMergeCount; i++) {CellRangeAddress cellAddresses = sheet.getMergedRegion(i);int firstColumn = cellAddresses.getFirstColumn();int lastColumn = cellAddresses.getLastColumn();int firstRow = cellAddresses.getFirstRow();int lastRow = cellAddresses.getLastRow();if (row >= firstRow && row <= lastRow) {if (column >= firstColumn && column <= lastColumn) {return true;}}}return false;}/*** 获取合并行的数据** @param sheet* @param row* @param column* @return*/private static String getMergedRegionValue(Sheet sheet, int row, int column) {// 获得该sheet所有合并单元格数量int sheetMergeCount = sheet.getNumMergedRegions();for (int i = 0; i < sheetMergeCount; i++) {// 获得合并区域CellRangeAddress cellAddresses = sheet.getMergedRegion(i);int firstColumn = cellAddresses.getFirstColumn();int lastColumn = cellAddresses.getLastColumn();int firstRow = cellAddresses.getFirstRow();int lastRow = cellAddresses.getLastRow();/*判断传入的单元格的行号列号是否在合并单元格的范围内,如果在合并单元格的范围内,择返回合并区域的首单元格格值*/if (row >= firstRow && row <= lastRow) {if (column >= firstColumn && column <= lastColumn) {Row firRow = sheet.getRow(firstRow);Cell firCell = firRow.getCell(firstColumn);return getCellValue(firCell);}}}// 如果该单元格行号列号不在任何一个合并区域,则返回nullreturn null;}/*** 获取行的集合* @param sheet* @param startRow* @param lastRowNum* @return*/private static List<String[]> rowList(Sheet sheet,int startRow,int lastRowNum){List<String[]> list = new ArrayList<>();for (int rowNum = startRow; rowNum <= lastRowNum; rowNum++) {//获得当前行Row row = sheet.getRow(rowNum);if (!Optional.ofNullable(row).isPresent()) {continue;}//获得当前行的开始列int firstColumn = row.getFirstCellNum();//获得当前行的列数int lastColumn = row.getPhysicalNumberOfCells();String[] cells = new String[lastColumn];//行内所有列的值//循环当前行的所有列for (int column = firstColumn; column < lastColumn; column++) {Cell cell = row.getCell(column);//遍历中的某列// 判断是否合并行boolean isMerge = isMergedRow(sheet, rowNum, column);if (isMerge) {//是合并行,则获取合并行数据cells[column] = getMergedRegionValue(sheet, rowNum, column);} else {cells[column] = getCellValue(cell);//默认当前列内值}}list.add(cells);}return list;}/*** 获取单元格值** @param cell* @return*/private static String getCellValue(Cell cell) {String cellValue = "";if (cell == null) {return cellValue;}// 把数字当成String来读,避免出现1读成1.0的情况if (cell.getCellType() == CellType.NUMERIC) {cell.setCellType(CellType.STRING);}// 判断数据的类型switch (cell.getCellType()) {// 文本case STRING:cellValue = cell.getStringCellValue();break;// 数字、日期case NUMERIC:if (DateUtil.isCellDateFormatted(cell)) {sdf = new SimpleDateFormat("yyyy/MM/dd");// 日期型cellValue = sdf.format(cell.getDateCellValue());} else {// 数字cellValue = String.valueOf(cell.getNumericCellValue());}break;// 布尔型case BOOLEAN:cellValue = String.valueOf(cell.getBooleanCellValue());break;// 空白case BLANK:cellValue = cell.getStringCellValue();break;// 错误case ERROR:cellValue = "";break;// 公式case FORMULA:cellValue = "";break;default:cellValue = "";}return cellValue;}}

==========================================

其他POI处理合并行的参考:(未验证是否可用)
https://www.cnblogs.com/zhou-pan/p/10037438.html

相关文章:

若依 ruoyi poi Excel合并行的导入

本文仅针对文字相关的合并做了处理 &#xff0c;图片合并及保存需要另做处理&#xff01;&#xff01; 目标&#xff1a;Excel合并行内容的导入 结果&#xff1a; 1. ExcelUtil.java 类&#xff0c;新增方法&#xff1a;判断是否是合并行 /*** 新增 合并行相关代码&#xff1a;…...

优化算法:1.遗传算法(GA)及Python实现

一、定义 遗传算法就像是在模拟“优胜劣汰”的进化过程&#xff0c;通过选择最优秀的个体&#xff0c;交配产生下一代&#xff0c;并引入一定的变异&#xff0c;逐步优化解决问题。 二、具体步骤 初始化种群(Initialization)&#xff1a; 假设你要找到一个迷宫的最佳出口路径。…...

企业化运维(8)Docker容器技术

###1.Docker介绍### 什么是Docker Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中&#xff0c;然后发布到任何流行的 Linux或Windows 机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之间…...

Unity C#底层原理(二)

委托 方法的容器&#xff1a;委托可以存储一个或多个方法的引用。可以使用委托对象来调用这些方法。函数/方法的变量类型&#xff1a;委托类型可以像变量一样声明和使用&#xff0c;存储方法的引用。存储、传递方法&#xff1a;委托可以作为参数传递给方法&#xff0c;也可以作…...

计算机网络-配置路由器ACL(访问控制列表)

配置访问控制列表ACL 拓扑结构 拓扑结构如下&#xff1a; 要配置一个ACL&#xff0c;禁止PC0访问PC3&#xff0c;禁止PC4访问PC0&#xff0c;其它正常。 配置Router0 配置接口IP地址&#xff1a; interface fastethernet 0/0 ip address 192.168.1.1 255.255.255.0 no shu…...

51单片机嵌入式开发:20、STC89C52R基于C51嵌入式点阵广告屏的设计

STC89C52R基于C51嵌入式点阵广告屏的设计 1 概述2 LED点阵介绍2.1 特点和优势2.2 工作原理&#xff1a;2.3 使用方法&#xff1a; 3 LED点阵原理3.1 Led点阵内部电路3.2 原理图电路3.3 74HC595 4 软件实现点阵图案的滑动4.1 软件工程代码4.2 Protues仿真 5 总结 配套示例程序 1…...

VLC输出NDI媒体流

目录 1. 下载安装VLC Play 2. 首先在电脑上安装NDI Tools 3. 运行VLC进行输出配置 4. 播放视频 5. 验证 (1)用Studio Monitor验证 (2)用OBS验证 NDI(Network Device Interface)即网络设备接口,是由美国 NewTek 公司开发的免费标准,它可使兼容的视频产品以高质量…...

WiFi 局域网通信 - 发现服务和解析

1. nsdManager nsdManager requireContext().getSystemService(Context.NSD_SERVICE) as NsdManager2. NsdManager.DiscoveryListener 注意&#xff1a;在onStartDiscoveryFailed 和 onStopDiscoveryFailed里不要调用nsdManager.stopServiceDiscovery(this) 方法&#xff0…...

ChatGPT建议前端学习计划

HTML&CSS基础 - 学习HTML标签、CSS属性、页面布局等基础知识 JavaScript基础 - 学习变量、数据类型、控制流、函数等基础知识 jQuery - 学习如何使用jQuery处理文档对象模型&#xff08;DOM&#xff09;、事件、动画等 Ajax - 全称为 Asynchronous JavaScript and XML&…...

YOLO5项目目录最强解析

YOLO5项目目录解析 YOLOv5 项目目录下的文件和目录的结构&#xff0c;以下是对每个目录和文件的解释&#xff1a; 目录 &#x1f4c1; .github: 存放 GitHub 相关配置和文件&#xff0c;如 GitHub Actions 工作流文件、Issue 模板等&#xff0c;用于自动化构建和持续集成等功…...

【python】sklearn基础教程及示例

【python】sklearn基础教程及示例 Scikit-learn&#xff08;简称sklearn&#xff09;是一个非常流行的Python机器学习库&#xff0c;提供了许多常用的机器学习算法和工具。以下是一个基础教程的概述&#xff1a; 1. 安装scikit-learn 首先&#xff0c;确保你已经安装了Python和…...

Linux:传输层(2) -- TCP协议(2)

目录 1. 流量控制 2. 滑动窗口 3. 拥塞控制 4. 延迟应答 5. 捎带应答 6. 面向字节流 7. 粘包问题 8. TCP异常情况 1. 流量控制 接收端处理数据的速度是有限的. 如果发送端发的太快 , 导致接收端的缓冲区被打满 , 这个时候如果发送端继续发送 , 就会造成丢包, 继而引…...

AcWing 802. 区间和

var说明add存储了插入操作&#xff0c;在指定 x x x下标所在位置 a [ x ] c a[x]c a[x]cquery是求 [ L , R ] [L,R] [L,R]区间和用到的数组,最后才用到alls 是存储离散化之后的值 , 对于会访问到的每个下标&#xff0c;统统丢到 a l l s 里面 &#xff0c;会把 x 和 [ L , R …...

实验2-2-1 温度转换

#include<stdio.h> #include <math.h> int main(){int c,f150;c5*(f-32)/9;printf("fahr 150, celsius %d",c); }...

Spark实时(六):Output Sinks案例演示

文章目录 Output Sinks案例演示 一、​​​​​​​File sink 二、​​​​​​​​​​​​​​Memory Sink 三、​​​​​​​​​​​​​​Foreach Sink 1、​​​​​​​foreachBatch 2、​​​​​​​​​​​​​​foreach Output Sinks案例演示 当我们对流式…...

在SQL编程中DROP、DELETE和TRUNCATE的区别

在SQL编程中&#xff0c;DROP、DELETE和TRUNCATE都是用于删除数据的命令&#xff0c;但它们之间有着显著的区别&#xff0c;主要体现在它们删除数据的范围、操作的不可逆性、对表结构的影响、性能以及事务日志的影响上。 DROP: 作用&#xff1a;DROP命令用于删除整个表及其所有…...

【AI大模型】Prompt 提示词工程使用详解

目录 一、前言 二、Prompt 提示词工程介绍 2.1 Prompt提示词工程是什么 2.1.1 Prompt 构成要素 2.2 Prompt 提示词工程有什么作用 2.2.1 Prompt 提示词工程使用场景 2.3 为什么要学习Prompt 提示词工程 三、Prompt 提示词工程元素构成与操作实践 3.1 前置准备 3.2 Pro…...

学习记录day18——数据结构 算法

算法的相关概念 程序 数据结构 算法 算法是程序设计的灵魂&#xff0c;结构式程序设计的肉体 算法&#xff1a;计算机解决问题的方法护额步骤 算法的特性 1、确定性&#xff1a;算法中每一条语句都有确定的含义&#xff0c;不能模棱两可 2、有穷性&#xff1a;程序执行一…...

一篇文章带你学完Java所有的时间与日期类

目录 一、传统时间与日期类 1.Date类 构造方法 获取日期和时间信息的方法 设置日期和时间信息的方法 2.Calendar类 主要特点和功能 常用方法 1. 获取当前日历对象 2. 获取日历中的某个信息 3. 获取日期对象 4. 获取时间毫秒值 5. 修改日历的某个信息 6. 为某个信息增…...

利用GPT4o Captcha工具和AI技术全面识别验证码

利用GPT4o Captcha工具和AI技术全面识别验证码 &#x1f9e0;&#x1f680; 摘要 GPT4o Captcha工具是一款命令行工具&#xff0c;通过Python和Selenium测试各种类型的验证码&#xff0c;包括拼图、文本、复杂文本和reCAPTCHA&#xff0c;并使用OpenAI GPT-4帮助解决验证码问…...

大学生算法高等数学学习平台设计方案 (第一版)

目录 目标用户群体的精准定位 初阶探索者 进阶学习者 资深研究者 功能需求的深度拓展 个性化学习路径定制 概念图谱构建 公式推导展示 交互式问题解决系统 新功能和创新点的引入 虚拟教室环境 数学建模工具集成 算法可视化平台 学术论文资源库 技术实现的前瞻性…...

机器学习算法与Python实战 | 两行代码即可应用 40 个机器学习模型--lazypredict 库!

本文来源公众号“机器学习算法与Python实战”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;两行代码即可应用 40 个机器学习模型 今天和大家一起学习使用 lazypredict 库&#xff0c;我们可以用一行代码在我们的数据集上实现许多…...

使用WebSocket协议调用群发方法将消息返回客户端页面

目录 一.C/S架构&#xff1a; 二.Http协议与WebSocket协议的区别&#xff1a; 1.Http协议与WebSocket协议的区别&#xff1a; 2.WebSocket协议的使用场景&#xff1a; 三.项目实际操作&#xff1a; 1.导入依赖&#xff1a; 2.通过WebSocket实现页面与服务端保持长连接&a…...

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十七章 Linux中断实验

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…...

每日一题~961div2A+B+C(阅读题,思维,数学log)

A 题意&#xff1a;给你 n*n 的表格和k 个筹码。每个格子上至多放一个 问至少占据多少对角线。 显然&#xff0c;要先 格数的多的格子去放。 n n-1 n-2 …1 只有n 的是一个&#xff08;主对角线&#xff09;&#xff0c;其他的是两个。 #include <bits/stdc.h> using na…...

Fireflyrk3288 ubuntu18.04添加Qt开发环境、安装mysql-server

1、创建一台同版本的ubuntu18.04的虚拟机 2、下载rk3288_ubuntu_18.04_armhf_ext4_v2.04_20201125-1538_DESKTOP.img 3、创建空img镜像容器 dd if/dev/zero ofubuntu_rootfs.img bs1M count102404、将该容器格式化成ext4文件系统 mkfs.ext4 ubuntu_rootfs.img5、将该镜像文件…...

简化mybatis @Select IN条件的编写

最近从JPA切换到Mybatis&#xff0c;使用无XML配置&#xff0c;Select注解直接写到interface上&#xff0c;发现IN条件的编写相当麻烦。 一般得写成这样&#xff1a; Select({"<script>","SELECT *", "FROM blog","WHERE id IN&quo…...

Windows图形界面(GUI)-MFC-C/C++ - Control

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 Control 资源编辑器 添加控件 设置控件属性 添加控件变量 添加消息处理 处理控件事件 控件焦点顺序 Control 资源编辑器 资源编辑器&#xff1a;用于可视化地编辑对话框和控件。…...

SQL Server数据库安全:策略制定与实践指南

SQL Server数据库安全&#xff1a;策略制定与实践指南 在当今数字化时代&#xff0c;数据安全是每个组织的核心关注点。SQL Server作为广泛使用的关系型数据库管理系统&#xff0c;提供了一套强大的安全特性来保护存储的数据。制定有效的数据库安全策略是确保数据完整性、可用…...

Spring Boot入门指南:留言板

一.留言板 1.输⼊留⾔信息,点击提交.后端把数据存储起来. 2.⻚⾯展⽰输⼊的表⽩墙的信息 规范&#xff1a; 1.写一个类MessageInfo对象&#xff0c;添加构造方法 虽然有快捷键&#xff0c;但是还是不够偷懒 项目添加Lombok。 Lombok是⼀个Java⼯具库&#xff0c;通过添加注…...

Docker 中安装和配置带用户名和密码保护的 Elasticsearch

在 Docker 中安装和配置带用户名和密码保护的 Elasticsearch 需要以下步骤。Elasticsearch 的安全功能&#xff08;包括基本身份验证&#xff09;在默认情况下是启用的&#xff0c;但在某些版本中可能需要手动配置。以下是详细步骤&#xff0c;包括如何设置用户名和密码。 1. …...

面试官:说说JVM内存调优及内存结构

1. JVM简介 JVM&#xff08;Java虚拟机&#xff09;是运行Java程序的平台&#xff0c;它使得Java能够跨平台运行。JVM负责内存的自动分配和回收&#xff0c;减轻了程序员的负担。 2. JVM内存结构 运行时数据区是JVM中最重要的部分&#xff0c;包含多个内存区域&#xff1a; …...

Ansible的脚本-----playbook剧本【下】

目录 实战演练六&#xff1a;tags 模块 实战演练七&#xff1a;Templates 模块 实战演练六&#xff1a;tags 模块 可以在一个playbook中为某个或某些任务定义“标签”&#xff0c;在执行此playbook时通过ansible-playbook命令使用--tags选项能实现仅运行指定的tasks。 playboo…...

Mysql开启远程控制简化版,亲测有效

首先关闭防火墙 改表法 打开上图的CMD&#xff0c;输入密码进入&#xff0c;然后输入一下指令 1.use mysql; 2.update user set host % where user root;//更新root用户的权限&#xff0c;允许任何主机连接 3.FLUSH PRIVILEGES;//刷新权限&#xff0c;使更改生效 具体参考…...

【MQTT协议与IoT通信】MQTT协议的使用和管理

MQTT协议与IoT通信&#xff1a;MQTT协议的使用和管理 目录 引言MQTT协议概述 什么是MQTTMQTT的工作原理 MQTT协议的关键特性 轻量级与高效性发布/订阅模式质量服务等级(QoS)持久会话安全性 MQTT协议的使用方法 设置MQTT Broker连接MQTT Client发布消息订阅主题断开连接 MQTT协…...

根据题意写出完整的css,html和js代码【购物车模块页面及功能实现】

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…...

AWS免费层之后:了解和管理您的云服务成本

Amazon Web Services (AWS) 为新用户提供了12个月的免费层服务&#xff0c;这是许多人开始使用云服务的绝佳方式。但是&#xff0c;当这一年结束后&#xff0c;您的AWS使用会如何变化&#xff1f;我们九河云通过本文将探讨免费层结束后的AWS成本情况&#xff0c;以及如何有效管…...

Linux定时同步系统时间到硬件时间

Linux定时同步系统时间到硬件时间 1. 系统时间、软件时间 系统时间 &#xff08;System Time&#xff09;&#xff1a; 一般说来就是我们执行 date命令看到的时间&#xff0c;linux系统下所有的时间调 用&#xff08;除了直接访问硬件时间的命令&#xff09;都是使用的这个时…...

网络编程——wireshark抓包、tcp粘包

目录 一、前言 1.1 什么是粘包 1.2 为什么UDP不会粘包 二、编写程序 文件树 客户端程序 服务器程序 tcp程序 头文件 makefile 三、 实验现象 四、改进实验 五、小作业 一、前言 最近在做网络芯片的驱动&#xff0c;验证功能的时候需要借助wireshark这个工具&…...

el-table合计行更新问题

说明&#xff1a;在使用el-table自带的底部合计功能时&#xff0c;初始界面不会显示合计内容 解决方案&#xff1a;使用 doLayout()方法 updated() {this.$nextTick(() > {this.$refs[inventorySumTable].doLayout();});},完整代码&#xff1a; // show-summary&#xff1a…...

ChatGPT:数据库不符合第二范式示例

ChatGPT&#xff1a;数据库不符合第二范式示例 这张图片为什么不符合数据库第二范式 这个表格不符合数据库第二范式&#xff08;2NF&#xff09;的原因如下&#xff1a; 1. 数据库第二范式&#xff08;2NF&#xff09;定义 第二范式要求一个数据库表格在满足第一范式&#xf…...

27、美国国家冰雪中心(NSIDC)海冰密集度月数据下载与处理

文章目录 一、前言二、数据下载三、使用Ponply查看数据结构四、代码一、前言 处理美国国家冰雪中心(NSIDC)的海冰密集度月度数据时,坐标转换是一个重要的步骤。NSIDC提供的数据通常采用极地球面坐标系,需要将其转换为常用的地理坐标系(如经纬度)以便进行分析和可视化。 坐…...

vite环境下使用bootstrap

环境 nodejs 18 pnpm 初始化 pnpm init pnpm add -D vite --registry http://registry.npm.taobao.org pnpm add bootstrap popperjs/core --registry http://registry.npm.taobao.org pnpm add -D sass --registry http://registry.npm.taobao.org新建vite.config.js cons…...

Laravel视图渲染封装

第一种 app/Helpers/ViewHelper.php 创建一个辅助函数&#xff0c;用于动态确定视图路径&#xff1a; <?php if (!function_exists(fetchView)) {function fetchView($data []){$currentAction \Route::currentRouteAction();list($controller, $method) explode(, $c…...

C++学习补充2:MySQL select 查询

MySQL select 查询 MySQL 查询 select时&#xff0c; 不区分大小写的。 MySQL 在默认情况下是区分大小写的&#xff0c;但是它的行为可能因配置和使用的字符集而有所不同。以下是一些可能导致查询在 SELECT 语句中不区分大小写的原因&#xff1a; 字符集设置&#xff1a;如果…...

uni-app声生命周期

应用的生命周期函数在App.vue页面 onLaunch:当uni-app初始化完成时触发&#xff08;全局触发一次&#xff09; onShow:当uni-app启动&#xff0c;或从后台进入前台时显示 onHide:当uni-app从前台进入后台 onError:当uni-app报错时触发,异常信息为err 页面的生命周期 onLoad…...

排序算法--堆排序

基本思想 堆排序的基本思想是&#xff0c;将待排序的元素构建成一个最大堆或最小堆。对于最大堆来说&#xff0c;堆顶是整个堆中的最大元素&#xff1b;对于最小堆来说&#xff0c;堆顶是整个堆中的最小元素。然后&#xff0c;将堆顶元素与堆中最后一个元素交换&#xff0c;并…...

iPhone 在 App Store 中推出的 PC 模拟器 UTM SE

PC 模拟器是什么&#xff1f;PC 模拟器是一种软件工具&#xff0c;它模拟不同硬件或操作系统环境&#xff0c;使得用户可以在一台 PC 上运行其他平台的应用程序或操作系统。通过 PC 模拟器&#xff0c;用户可以在 Windows 电脑上体验 Android 应用、在 Mac 电脑上运行 Windows …...

FastAPI删除mongodb重复数据(数据清洗)

在 FastAPI 中删除 MongoDB 重复数据&#xff0c;你需要结合使用 MongoDB 查询和 FastAPI 的路由功能。以下是一个通用的例子&#xff0c;演示如何删除特定字段上的重复数据&#xff1a; 1. 定义数据模型: from pydantic import BaseModel, Field from bson import ObjectId …...

移动UI:排行榜单页面如何设计,从这五点入手,附示例。

移动UI的排行榜单页面设计需要考虑以下几个方面&#xff1a; 1. 页面布局&#xff1a; 排行榜单页面的布局应该清晰明了&#xff0c;可以采用列表的形式展示排行榜内容&#xff0c;同时考虑到移动设备的屏幕大小&#xff0c;应该设计合理的滚动和分页机制&#xff0c;确保用户…...