POI创建Excel文件
文章目录
- 1、背景
- 2、创建表格
- 2.1 定义表头对象
- 2.2 Excel生成器
- 2.3 创建模板
- 2.4 处理Excel表头
- 2.5 处理Excel内容单元格样式
- 2.6 处理单个表头
- 3、追加sheet
- 4、静态工具
- 5、单元测试
- 6、完整代码示例
1、背景
需求中有需要用户自定义Excel表格表头,然后生成Excel文件,使用EasyExcel更适合生成固定表头的Excel文档,所以此处采用POI原生方式进行开发。文档如下:
2、创建表格
主要的代码逻辑如下,非主要方法可以在完整代码中找到。
2.1 定义表头对象
根据需求,表头需要制定2级表头,我们先定义一个Excel表头对象。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExcelModelDto {/*** 名称 */private String fieldName;/*** 提示语 */private String comment;/*** 类型 */private Integer type;/*** 背景色 */private short backgroundColor;/*** 子标题 */private List<Child> children;@Data@NoArgsConstructor@AllArgsConstructorpublic static class Child {/*** 字段编码 */private String fieldCode;/*** 字段名称 */private String fieldName;/*** 提示语 */private String comment;/*** 类型 */private Integer type;/*** 下拉框选项 */private String[] items;}}
2.2 Excel生成器
创建一个Excel文件生成对象,包含多个属性,其中包括:文件路径、文件名称、是否需要下拉框、文件后缀名、最大文本行数等。
@Slf4j
public class ExcelGenerator {private final String localPath;private final String sheetName;private final String fileName;private final String file;private final Boolean needItems;private final List<ExcelModelDto> data;/*** 字段编码集合,从data中解析 */private final List<String> fieldCodeList;public static final Integer FIRST_ROW = 2;public static final Integer LAST_ROW = 65535;public static final String FILE_SUFFIX = ".xlsx";public static final String PATH_SUFFIX = "/";public static final String ITEM_SHEET_NAME = "itemSheet";public static final String END_FLAG = "*";public static final Integer MAX_CONTENT_ROW_NUMBER = 1002;/*** 扩展字段sheet页行数记录key值*/public static final String EXTEND_PAGE_ROW_NUMBER_KEY = "extend";public ExcelGenerator(String localPath, String fileName, String sheetName, List<ExcelModelDto> data) {this(localPath, fileName, sheetName, true, data);}public ExcelGenerator(String localPath, String fileName, String sheetName, Boolean needItems, List<ExcelModelDto> data) {this.localPath = localPath;this.fileName = fileName;this.sheetName = sheetName;this.file = localPath + fileName;this.needItems = needItems;this.data = data;fieldCodeList = this.parseField(data);}
}
2.3 创建模板
/*** 生成模板** @throws IOException 异常*/public void createTemplate() throws IOException {this.doCreateSheet(Paths.get(file), sheetName, data);}/*** 向Excel文件新增一个新的工作表,并处理表头。** @param pathForFile 新工作表将要保存的文件路径。* @throws IOException 如果读写文件时发生异常。*/private void doCreateSheet(Path pathForFile, String sheetName, List<ExcelModelDto> data)throws IOException {long startTime = System.currentTimeMillis();Workbook workbook = new XSSFWorkbook();Sheet sheet = this.getSheetByName(workbook, sheetName, false);// 处理Excel表头this.dealExcelHeadingCell(workbook, sheet, data);// 处理Excel内容单元格,默认都是有二级标题this.dealExcelContentCell(workbook, sheet, data);// 将inputStream转换为outputStream,并重新写入文件try (OutputStream outputStream = Files.newOutputStream(pathForFile)) {workbook.write(outputStream);} finally {long endTime = System.currentTimeMillis();log.info("创建Excel模板文件共耗时:{}秒。", (endTime - startTime) / 1000);}}
2.4 处理Excel表头
/*** 处理 Excel 表头数据,包括第一行和第二行的标题单元格样式设置、数据填充和合并单元格。** @param workbook 工作簿对象* @param sheet 主表的工作表对象* @param data 表头数据*/private void dealExcelHeadingCell(Workbook workbook, Sheet sheet, List<ExcelModelDto> data) {// 创建第一行和第二行表头数据,并设置行高Row row1 = this.getRow(sheet, 0);Row row2 = this.getRow(sheet, 1);row1.setHeightInPoints(20);row2.setHeightInPoints(20);// 已经存在的列号int lastCellNum = this.getLastCellNum(sheet, 1);int currentCellNum = lastCellNum;int startCellNum = lastCellNum;int endCellNum;for (ExcelModelDto excelModelDto : data) {// 一级标题名称String firstTitleName = excelModelDto.getFieldName();// 一级标题单元格样式CellStyle firstTitleCellStyle = this.buildFirstTitleCellStyle(workbook, excelModelDto);// 二级标题的单元格样式CellStyle secondTitleCellStyle = this.getCellStyle(workbook, IndexedColors.WHITE.getIndex());List<ExcelModelDto.Child> children = excelModelDto.getChildren();if (children == null || children.size() == 0) {continue;}for (ExcelModelDto.Child child : children) {// 处理表头单元格this.dealTitleCell(workbook, sheet, child, firstTitleName, firstTitleCellStyle, secondTitleCellStyle, currentCellNum);// 处理完后列号加一currentCellNum++;}endCellNum = currentCellNum - 1;// POI 版本升级后,合并单元格需要大于一个单元格if (startCellNum != endCellNum) {CellRangeAddress region = new CellRangeAddress(0, 0, startCellNum, endCellNum);sheet.addMergedRegion(region);}startCellNum = endCellNum + 1;}}
2.5 处理Excel内容单元格样式
/*** 格式化内容单元格。** @param sheet 工作表对象。* @param workbook 工作簿对象。*/private void dealExcelContentCell(Workbook workbook, Sheet sheet, List<ExcelModelDto> data) {// 获取统一的单元格样式,不用每个单元格获取一个对象,防止对象过多CellStyle childCellStyle = this.getContentCellStyle(workbook);// 只格式化内容单元格,且有上限int maxContentRowNumber = MAX_CONTENT_ROW_NUMBER;// 跳过表头,从文本行开始for (int rowNumber = 2; rowNumber < maxContentRowNumber; rowNumber++) {Row row = sheet.createRow(rowNumber);// 列号从0开始int cellNumber = 0;for (ExcelModelDto excelModelDto : data) {List<ExcelModelDto.Child> children = excelModelDto.getChildren();for (ExcelModelDto.Child child : children) {String[] items = child.getItems();if (Objects.isNull(items) || items.length == 0) {Cell cell = row.createCell(cellNumber);cell.setCellStyle(childCellStyle);}// 每处理完一个单元格,列号加1cellNumber++;}}}}
2.6 处理单个表头
在处理表头过程中,如果items 不为空,则说明此列需要下拉框,数组为供用户选择的下拉内容,防止下拉框内容过大,所以将下拉内容单独生成到一个隐藏的sheet页中,并且使用表达式来表达下拉框内容,设定到单元格中。
/*** 处理Excel表格的标题单元格。** @param workbook 工作簿对象* @param sheet 工作表对象* @param child ExcelModelDto.Child 对象,包含字段名、注释和下拉框选项等信息* @param firstTitleName 一级标题名称* @param firstTitleCellStyle 一级标题单元格样式* @param secondTitleCellStyle 二级标题单元格样式* @param index 当前处理的列索引*/private void dealTitleCell(Workbook workbook, Sheet sheet,ExcelModelDto.Child child, String firstTitleName,CellStyle firstTitleCellStyle, CellStyle secondTitleCellStyle,int index) {Row row1 = this.getRow(sheet, 0);Row row2 = this.getRow(sheet, 1);String secondFieldName = child.getFieldName();String comment = child.getComment();String[] items = child.getItems();// 一级表头Cell cell1 = row1.createCell(index);cell1.setCellValue(firstTitleName);cell1.setCellStyle(firstTitleCellStyle);// 二级表头,标题如果以* 号结尾,则* 置为红色Cell cell2 = row2.createCell(index);RichTextString textString = this.parseCellValue(workbook, Font.COLOR_NORMAL, true, secondFieldName);cell2.setCellValue(textString);cell2.setCellStyle(secondTitleCellStyle);// 设置下拉框if (items != null && items.length > 0 && needItems) {this.appendItems(workbook, sheet, secondTitleCellStyle, secondFieldName, items, index);}// 设置表头备注if (!org.apache.commons.lang.StringUtils.isEmpty(comment)) {this.setComment(sheet, cell2, comment);}// 根据字段长度自动调整列的宽度sheet.setColumnWidth(index, 100 * 50);}/*** 在指定的工作簿和工作表中追加枚举类型的项,并设置公式引用。** @param workbook 工作簿对象* @param sheet 工作表对象* @param childCellStyle 子单元格样式* @param secondTitleName 第二级标题名称* @param items 枚举类型的项数组* @param index 当前项在总体中的索引位置*/private void appendItems(Workbook workbook, Sheet sheet, CellStyle childCellStyle, String secondTitleName, String[] items, int index) {// 如果有序列单元格,则创建一个sheet页,来保存所有的枚举类型,同时隐藏该sheet页Sheet itemsSheet = this.getSheetByName(workbook, ITEM_SHEET_NAME, true);// 追加sheet的时候,需要看隐藏sheet的列已经到哪一列了,避免追加时将原有隐藏列覆盖掉int existItemCell = this.getLastCellNum(itemsSheet, 0);// 将枚举数组写入到独立的sheet页中,同时设置表头格式String formula = this.writeItems(itemsSheet, childCellStyle, secondTitleName, existItemCell, items);// 设置公式到模板的sheet页中,格式化后的最终公式为// =itemSheet!$B$1:$B$88// 表明该单元格引用的是 itemSheet sheet页中 B1~B88的数据formula = String.format("=%s!%s", ITEM_SHEET_NAME, formula);this.setItems(sheet, formula, FIRST_ROW, LAST_ROW, index, index);}
3、追加sheet
有些需要在已有的Excel文档中追加新的sheet表格内容,效果如下:
/*** 在指定的 Excel 文件中添加一个新的工作表,并填充数据。** @param sheetName 新工作表的名称* @param data 要填充的数据列表* @throws IOException 如果在操作文件时发生了 I/O 错误*/public void appendSheet(String sheetName, List<ExcelModelDto> data) throws IOException {long startTime = System.currentTimeMillis();// 路径不存在则创建,保证路径是存在的Path pathForLocalPath = Paths.get(localPath);boolean existPath = Files.exists(pathForLocalPath);if (!existPath) {Files.createDirectories(pathForLocalPath);}// 如果文件不存在,则走创建sheet逻辑Path pathForFile = Paths.get(file);if (!Files.exists(pathForFile)) {this.doCreateSheet(pathForFile, sheetName, data);return;}// 如果文件存在则走追加sheet逻辑try (InputStream inputStream = Files.newInputStream(pathForFile)) {this.doAppendSheet(inputStream, pathForFile, sheetName, data);long endTime = System.currentTimeMillis();log.info("追加Excel模板文件共耗时:{}秒。", (endTime - startTime) / 1000);} catch (Exception e) {log.error("追加Excel模板文件失败!", e);throw new BizException(e);}}/*** 向Excel文件追加一个新的工作表,并处理表头。** @param inputStream Excel文件的输入流。* @param pathForFile 新工作表将要保存的文件路径。* @throws IOException 如果读写文件时发生异常。*/private void doAppendSheet(InputStream inputStream, Path pathForFile, String sheetName, List<ExcelModelDto> data)throws IOException {Workbook workbook = new XSSFWorkbook(inputStream);Sheet sheet = this.getSheetByName(workbook, sheetName, false);// 处理Excel表头this.dealExcelHeadingCell(workbook, sheet, data);// 处理Excel内容单元格,默认都是有二级标题this.dealExcelContentCell(workbook, sheet, data);// 将inputStream转换为outputStream,并重新写入文件try (OutputStream outputStream = Files.newOutputStream(pathForFile)) {IOUtils.copy(inputStream, outputStream);workbook.write(outputStream);}}
4、静态工具
每次使用都需要new一个对象来创建Excel文件,所以创建一个静态工具类,来通过静态方法实现文档的创建与追加。
public class ExcelGeneratorExecutors {/*** 创建 Excel 模板文件。** @param localPath 本地路径* @param fileName 文件名* @param sheetName 工作表名称* @param data 数据列表* @throws IOException 如果创建模板文件失败*/public static void createTemplate(String localPath, String fileName, String sheetName, List<ExcelModelDto> data) throws IOException {ExcelGenerator excelGenerator = new ExcelGenerator(localPath, fileName, sheetName, data);excelGenerator.createTemplate();}/*** 在指定路径的Excel文件中追加一个新的工作表,并填充数据。** @param localPath Excel文件的本地路径。* @param fileName Excel文件的名称。* @param sheetName 新增工作表的名称。* @param data 填充到新增工作表的数据。* @throws IOException 如果在追加工作表或填充数据时发生I/O错误。*/public static void appendSheet(String localPath, String fileName, String sheetName, List<ExcelModelDto> data) throws IOException {ExcelGenerator excelGenerator = new ExcelGenerator(localPath, fileName, sheetName, data);excelGenerator.appendSheet(sheetName, data);}}
5、单元测试
@Testpublic void testGenerate() {String localPath = "D:\\mytmp\\template\\";String dateTime = DateUtils.format(new Date(), DateUtils.DATE_FORMAT_COMMENT_2);String fileName = String.format("生成模板-%s.xlsx", dateTime);String sheetName = "测试";List<ExcelModelDto> data = this.buildExcelModelDtoList();ExcelGenerator excelGenerator = new ExcelGenerator(localPath, fileName, sheetName, data);try {excelGenerator.createTemplate();List<ExcelModelDto> data2 = this.buildExcelModelDtoList2();excelGenerator.appendSheet("自定义sheet", data);excelGenerator.appendSheet("自定义sheet2", data2);excelGenerator.appendSheet("自定义sheet3", data2);log.info("模板文件生成,名称为:{}", fileName);} catch (IOException e) {e.printStackTrace();}}@Testpublic void testGenerate2() {String localPath = "D:\\mytmp\\template\\";String dateTime = DateUtils.format(new Date(), DateUtils.DATE_FORMAT_COMMENT_2);String fileName = String.format("生成模板-%s.xlsx", dateTime);String sheetName = "测试";List<ExcelModelDto> data = this.buildExcelModelDtoList();try {ExcelGeneratorExecutors.createTemplate(localPath, fileName, sheetName, data);ExcelGeneratorExecutors.appendSheet(localPath, fileName, sheetName, data);ExcelGeneratorExecutors.appendSheet(localPath, fileName, "自定义sheet3", data);log.info("模板文件生成,名称为:{}", fileName);} catch (IOException e) {e.printStackTrace();}}public List<ExcelModelDto> buildExcelModelDtoList() {List<ExcelModelDto> data = new ArrayList<>();ExcelModelDto excelModelDto = new ExcelModelDto();excelModelDto.setFieldName("电器");excelModelDto.setComment("song");excelModelDto.setType(2);excelModelDto.setBackgroundColor((short) 2);List<ExcelModelDto.Child> children = new ArrayList<>();ExcelModelDto.Child child1 = new ExcelModelDto.Child();child1.setComment("类目1");child1.setFieldCode("category");child1.setFieldName("类目1");List<String> list1 = Lists.newArrayList("冰箱", "洗衣机", "空调");child1.setItems(list1.toArray(new String[0]));ExcelModelDto.Child child2 = new ExcelModelDto.Child();child2.setComment("数量1");child2.setFieldCode("qty");child2.setFieldName("数量1");List<String> list2 = Lists.newArrayList("1", "2", "3");child2.setItems(list2.toArray(new String[0]));ExcelModelDto.Child child3 = new ExcelModelDto.Child();child3.setComment("文本内容");child3.setFieldCode("textValue");child3.setFieldName("文本内容");children.add(child1);children.add(child2);children.add(child3);excelModelDto.setChildren(children);data.add(excelModelDto);return data;}public List<ExcelModelDto> buildExcelModelDtoList2() {List<ExcelModelDto> data = new ArrayList<>();ExcelModelDto excelModelDto0 = new ExcelModelDto();excelModelDto0.setFieldName("商家运单号");excelModelDto0.setComment("商家运单号");excelModelDto0.setType((int) IndexedColors.TURQUOISE1.getIndex());excelModelDto0.setBackgroundColor(IndexedColors.TURQUOISE1.getIndex());ExcelModelDto.Child child0 = new ExcelModelDto.Child();child0.setComment("关联第一个sheet页的商家运单号");child0.setFieldCode("orderNo");child0.setFieldName("商家运单号*");List<ExcelModelDto.Child> children0 = new ArrayList<>();children0.add(child0);excelModelDto0.setChildren(children0);ExcelModelDto excelModelDto = new ExcelModelDto();excelModelDto.setFieldName("购买电器");excelModelDto.setComment("song");excelModelDto.setType((int) IndexedColors.TURQUOISE1.getIndex());excelModelDto.setBackgroundColor(IndexedColors.TURQUOISE1.getIndex());ExcelModelDto.Child child1 = new ExcelModelDto.Child();child1.setComment("类目");child1.setFieldCode("category");child1.setFieldName("类目");List<String> list1 = Lists.newArrayList("冰箱", "洗衣机", "空调");child1.setItems(list1.toArray(new String[0]));ExcelModelDto.Child child2 = new ExcelModelDto.Child();child2.setComment("数量");child2.setFieldCode("qty");child2.setFieldName("数量");//List<String> list2 = Lists.newArrayList("1", "2", "3");//child2.setItems(list2.toArray(new String[0]));List<ExcelModelDto.Child> children = new ArrayList<>();children.add(child1);children.add(child2);excelModelDto.setChildren(children);data.add(excelModelDto0);data.add(excelModelDto);return data;}
6、完整代码示例
@Slf4j
public class ExcelGenerator {private final String localPath;private final String sheetName;private final String fileName;private final String file;private final Boolean needItems;private final List<ExcelModelDto> data;/*** 字段编码集合,从data中解析 */private final List<String> fieldCodeList;public static final Integer FIRST_ROW = 2;public static final Integer LAST_ROW = 65535;public static final String FILE_SUFFIX = ".xlsx";public static final String PATH_SUFFIX = "/";public static final String ITEM_SHEET_NAME = "itemSheet";public static final String END_FLAG = "*";public static final Integer MAX_CONTENT_ROW_NUMBER = 1002;/*** 扩展字段sheet页行数记录key值*/public static final String EXTEND_PAGE_ROW_NUMBER_KEY = "extend";public ExcelGenerator(String localPath, String fileName, String sheetName, List<ExcelModelDto> data) {this(localPath, fileName, sheetName, true, data);}public ExcelGenerator(String localPath, String fileName, String sheetName, Boolean needItems, List<ExcelModelDto> data) {this.localPath = localPath;this.fileName = fileName;this.sheetName = sheetName;this.file = localPath + fileName;this.needItems = needItems;this.data = data;fieldCodeList = this.parseField(data);}/*** 创建对象时,将ExcelModel中的字段按顺序排好,保存到List中** @param data 入参* @return 返回值*/public List<String> parseField(List<ExcelModelDto> data) {List<String> fieldCodeList = new ArrayList<>();for (ExcelModelDto modelDto : data) {List<ExcelModelDto.Child> children = modelDto.getChildren();for (ExcelModelDto.Child child : children) {String fieldCode = child.getFieldCode();fieldCodeList.add(fieldCode);}}return fieldCodeList;}/*** 生成模板** @throws IOException 异常*/public void createTemplate() throws IOException {this.doCreateSheet(Paths.get(file), sheetName, data);}/*** 在指定的 Excel 文件中添加一个新的工作表,并填充数据。** @param sheetName 新工作表的名称* @param data 要填充的数据列表* @throws IOException 如果在操作文件时发生了 I/O 错误*/public void appendSheet(String sheetName, List<ExcelModelDto> data) throws IOException {long startTime = System.currentTimeMillis();// 路径不存在则创建,保证路径是存在的Path pathForLocalPath = Paths.get(localPath);boolean existPath = Files.exists(pathForLocalPath);if (!existPath) {Files.createDirectories(pathForLocalPath);}// 如果文件不存在,则走创建sheet逻辑Path pathForFile = Paths.get(file);if (!Files.exists(pathForFile)) {this.doCreateSheet(pathForFile, sheetName, data);return;}// 如果文件存在则走追加sheet逻辑try (InputStream inputStream = Files.newInputStream(pathForFile)) {this.doAppendSheet(inputStream, pathForFile, sheetName, data);long endTime = System.currentTimeMillis();log.info("追加Excel模板文件共耗时:{}秒。", (endTime - startTime) / 1000);} catch (Exception e) {log.error("追加Excel模板文件失败!", e);throw new BizException(e);}}/*** 向Excel文件新增一个新的工作表,并处理表头。** @param pathForFile 新工作表将要保存的文件路径。* @throws IOException 如果读写文件时发生异常。*/private void doCreateSheet(Path pathForFile, String sheetName, List<ExcelModelDto> data)throws IOException {long startTime = System.currentTimeMillis();Workbook workbook = new XSSFWorkbook();Sheet sheet = this.getSheetByName(workbook, sheetName, false);// 处理Excel表头this.dealExcelHeadingCell(workbook, sheet, data);// 处理Excel内容单元格,默认都是有二级标题this.dealExcelContentCell(workbook, sheet, data);// 将inputStream转换为outputStream,并重新写入文件try (OutputStream outputStream = Files.newOutputStream(pathForFile)) {workbook.write(outputStream);} finally {long endTime = System.currentTimeMillis();log.info("创建Excel模板文件共耗时:{}秒。", (endTime - startTime) / 1000);}}/*** 向Excel文件追加一个新的工作表,并处理表头。** @param inputStream Excel文件的输入流。* @param pathForFile 新工作表将要保存的文件路径。* @throws IOException 如果读写文件时发生异常。*/private void doAppendSheet(InputStream inputStream, Path pathForFile, String sheetName, List<ExcelModelDto> data)throws IOException {Workbook workbook = new XSSFWorkbook(inputStream);Sheet sheet = this.getSheetByName(workbook, sheetName, false);// 处理Excel表头this.dealExcelHeadingCell(workbook, sheet, data);// 处理Excel内容单元格,默认都是有二级标题this.dealExcelContentCell(workbook, sheet, data);// 将inputStream转换为outputStream,并重新写入文件try (OutputStream outputStream = Files.newOutputStream(pathForFile)) {IOUtils.copy(inputStream, outputStream);workbook.write(outputStream);}}/*** 处理 Excel 表头数据,包括第一行和第二行的标题单元格样式设置、数据填充和合并单元格。** @param workbook 工作簿对象* @param sheet 主表的工作表对象* @param data 表头数据*/private void dealExcelHeadingCell(Workbook workbook, Sheet sheet, List<ExcelModelDto> data) {// 创建第一行和第二行表头数据,并设置行高Row row1 = this.getRow(sheet, 0);Row row2 = this.getRow(sheet, 1);row1.setHeightInPoints(20);row2.setHeightInPoints(20);// 已经存在的列号int lastCellNum = this.getLastCellNum(sheet, 1);int currentCellNum = lastCellNum;int startCellNum = lastCellNum;int endCellNum;for (ExcelModelDto excelModelDto : data) {// 一级标题名称String firstTitleName = excelModelDto.getFieldName();// 一级标题单元格样式CellStyle firstTitleCellStyle = this.buildFirstTitleCellStyle(workbook, excelModelDto);// 二级标题的单元格样式CellStyle secondTitleCellStyle = this.getCellStyle(workbook, IndexedColors.WHITE.getIndex());List<ExcelModelDto.Child> children = excelModelDto.getChildren();if (children == null || children.size() == 0) {continue;}for (ExcelModelDto.Child child : children) {// 处理表头单元格this.dealTitleCell(workbook, sheet, child, firstTitleName, firstTitleCellStyle, secondTitleCellStyle, currentCellNum);// 处理完后列号加一currentCellNum++;}endCellNum = currentCellNum - 1;// POI 版本升级后,合并单元格需要大于一个单元格if (startCellNum != endCellNum) {CellRangeAddress region = new CellRangeAddress(0, 0, startCellNum, endCellNum);sheet.addMergedRegion(region);}startCellNum = endCellNum + 1;}}/*** 格式化内容单元格。** @param sheet 工作表对象。* @param workbook 工作簿对象。*/private void dealExcelContentCell(Workbook workbook, Sheet sheet, List<ExcelModelDto> data) {// 获取统一的单元格样式,不用每个单元格获取一个对象,防止对象过多CellStyle childCellStyle = this.getContentCellStyle(workbook);// 只格式化内容单元格,且有上限int maxContentRowNumber = MAX_CONTENT_ROW_NUMBER;// 跳过表头,从文本行开始for (int rowNumber = 2; rowNumber < maxContentRowNumber; rowNumber++) {Row row = sheet.createRow(rowNumber);// 列号从0开始int cellNumber = 0;for (ExcelModelDto excelModelDto : data) {List<ExcelModelDto.Child> children = excelModelDto.getChildren();for (ExcelModelDto.Child child : children) {String[] items = child.getItems();if (Objects.isNull(items) || items.length == 0) {Cell cell = row.createCell(cellNumber);cell.setCellStyle(childCellStyle);}// 每处理完一个单元格,列号加1cellNumber++;}}}}/*** 处理Excel表格的标题单元格。** @param workbook 工作簿对象* @param sheet 工作表对象* @param child ExcelModelDto.Child 对象,包含字段名、注释和下拉框选项等信息* @param firstTitleName 一级标题名称* @param firstTitleCellStyle 一级标题单元格样式* @param secondTitleCellStyle 二级标题单元格样式* @param index 当前处理的列索引*/private void dealTitleCell(Workbook workbook, Sheet sheet,ExcelModelDto.Child child, String firstTitleName,CellStyle firstTitleCellStyle, CellStyle secondTitleCellStyle,int index) {Row row1 = this.getRow(sheet, 0);Row row2 = this.getRow(sheet, 1);String secondFieldName = child.getFieldName();String comment = child.getComment();String[] items = child.getItems();// 一级表头Cell cell1 = row1.createCell(index);cell1.setCellValue(firstTitleName);cell1.setCellStyle(firstTitleCellStyle);// 二级表头,标题如果以* 号结尾,则* 置为红色Cell cell2 = row2.createCell(index);RichTextString textString = this.parseCellValue(workbook, Font.COLOR_NORMAL, true, secondFieldName);cell2.setCellValue(textString);cell2.setCellStyle(secondTitleCellStyle);// 设置下拉框if (items != null && items.length > 0 && needItems) {this.appendItems(workbook, sheet, secondTitleCellStyle, secondFieldName, items, index);}// 设置表头备注if (!org.apache.commons.lang.StringUtils.isEmpty(comment)) {this.setComment(sheet, cell2, comment);}// 根据字段长度自动调整列的宽度sheet.setColumnWidth(index, 100 * 50);}/*** 设置单元格下拉框* 下拉框引用单独一个sheet页中的数据** @param sheet sheet* @param formula 公式* @param firstRow 起始行* @param lastRow 结束行* @param firstCol 起始列* @param lastCol 结束列*/public void setItems(Sheet sheet, String formula, int firstRow, int lastRow, int firstCol, int lastCol) {CellRangeAddressList addressList = new CellRangeAddressList(firstRow, lastRow, firstCol, lastCol);DataValidationHelper helper = sheet.getDataValidationHelper();DataValidationConstraint constraint = helper.createFormulaListConstraint(formula);DataValidation validation = helper.createValidation(constraint, addressList);validation.setShowErrorBox(true);sheet.addValidationData(validation);}/*** 设置单元格备注信息** @param sheet sheet* @param cell 单元格* @param textString 提示信息*/public void setComment(Sheet sheet, Cell cell, String textString) {Drawing<?> drawing = sheet.createDrawingPatriarch();CreationHelper factory = sheet.getWorkbook().getCreationHelper();// 设置提示框大小,默认根据 提示信息的大小来确认提示框高度/// ClientAnchor anchor = factory.createClientAnchor();textString = StringUtils.defaultIfBlank(textString, "");int length = textString.length();int row2 = length / 25 + 6;// (int dx1, int dy1, int dx2, int dy2, short col1, int row1, short col2, int row2)// 前四个参数是坐标点,后四个参数是编辑和显示批注时的大小.ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) 4, 2, (short) 6, row2);Comment comment = drawing.createCellComment(anchor);RichTextString str = factory.createRichTextString(textString);comment.setString(str);comment.setAuthor("Auto+");// 以上参数不设置时会有默认值,当一个被重复设置批注时会报错// Multiple cell comments in one cell are not allowed// 故在设置批注前检查锚点位置有无批注,有的话移除if (cell.getCellComment() != null) {cell.removeCellComment();}cell.setCellComment(comment);}/*** 获取单元格样式对象** @param workbook 工作簿* @param backGroundColor 背景色* @return 返回样式对象*/public CellStyle getCellStyle(Workbook workbook, short backGroundColor) {CellStyle cellStyle = workbook.createCellStyle();CreationHelper createHelper = workbook.getCreationHelper();cellStyle.setDataFormat(createHelper.createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss"));// IndexedColors.YELLOW.getIndex()cellStyle.setFillForegroundColor(backGroundColor);cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);// 水平居中cellStyle.setAlignment(HorizontalAlignment.CENTER);// 垂直居中cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);// 设置边框及颜色cellStyle.setBorderTop(BorderStyle.DOUBLE);cellStyle.setBorderBottom(BorderStyle.DOUBLE);cellStyle.setBorderLeft(BorderStyle.DOUBLE);cellStyle.setBorderRight(BorderStyle.DOUBLE);cellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());cellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());cellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());cellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());return cellStyle;}/*** 向sheet中写入 序列内容** @param sheet sheet* @param cellStyle 单元格格式,表头格式* @param itemsType 序列类型* @param col 列号* @param items 序列数组* @return 返回坐标*/protected String writeItems(Sheet sheet, CellStyle cellStyle, String itemsType, int col, String[] items) {// 第一行为表头数据Row row = sheet.getRow(0);if (row == null) {row = sheet.createRow(0);}Cell cell = row.createCell(col);// 获取单元格列所对应的字母,即 0=A,1=B ...String columnLetter = CellReference.convertNumToColString(col);cell.setCellValue(itemsType);cell.setCellStyle(cellStyle);int length = items.length;for (int i = 0; i < length; i++) {Row itemRow = sheet.getRow(i + 1);if (itemRow == null) {itemRow = sheet.createRow(i + 1);}Cell itemRowCell = itemRow.createCell(col);itemRowCell.setCellValue(items[i]);}// 格式化后的公式坐标为 $B$1:$B$88return String.format("$%s$%s:$%s$%s", columnLetter, 2, columnLetter, items.length + 1);}/*** 格式化单元格字体样式** @param workbook 工作簿* @param fontColor 字体颜色* @param isBold 是否加粗* @param value 单元格值*/public RichTextString parseCellValue(Workbook workbook, short fontColor, boolean isBold, String value) {value = StringUtils.defaultIfBlank(value, "");XSSFRichTextString textString = new XSSFRichTextString(value);Font font1 = getFontStyle(workbook, fontColor, isBold);if (StringUtils.isNotBlank(value)) {int length = value.length();// 如果内容是以 * 号结尾的,则将 * 号置为红色,默认黑色if (value.endsWith(END_FLAG)) {int point = length - 1;textString.applyFont(0, point, font1);Font font2 = getFontStyle(workbook, Font.COLOR_RED, isBold);textString.applyFont(point, length, font2);} else {textString.applyFont(0, length, font1);}}return textString;}/*** 获取字体样式** @param workbook 工作簿* @param fontColor 字体颜色* @param isBold 是否加粗* @return 返回值*/public Font getFontStyle(Workbook workbook, short fontColor, boolean isBold) {Font font = workbook.createFont();font.setColor(fontColor);if (isBold) {font.setBold(true);}font.setFontName("宋体");// 字体大小font.setFontHeightInPoints((short) 10);return font;}/*** 获取指定行在给定工作表中的最后一个单元格的索引。** @param sheet 工作表对象* @param rowNum 行号(从0开始计数)* @return 最后一个单元格的索引,若行不存在则返回0*/private int getLastCellNum(Sheet sheet, int rowNum) {int existCell = 0;// 指定sheet页不为空,则获取已经有多少列Row row = sheet.getRow(rowNum);if (Objects.nonNull(row)) {existCell = row.getLastCellNum();// 如果不存在返回的是-1,业务上从0开始计算if (existCell < 0) {existCell = 0;}}return existCell;}/*** 获取或创建指定名称的工作表并将其隐藏。** @param workbook 工作簿对象* @return 指定名称的工作表对象*/private Sheet getSheetByName(Workbook workbook, String sheetName, boolean hide) {Sheet itemsSheet = workbook.getSheet(sheetName);// 指定sheet页为空则创建if (Objects.isNull(itemsSheet)) {itemsSheet = workbook.createSheet(sheetName);int sheetIndex = workbook.getSheetIndex(sheetName);workbook.setSheetHidden(sheetIndex, hide);}return itemsSheet;}/*** 根据行号获取或创建指定Sheet中的Row对象。** @param sheet 要操作的Sheet对象。* @param rowNum 需要获取或创建的行号。* @return 指定行号的Row对象。*/private Row getRow(Sheet sheet, int rowNum) {Row row = sheet.getRow(rowNum);if (Objects.isNull(row)) {row = sheet.createRow(rowNum);}return row;}/*** 构建第一行标题单元格样式。** @param workbook 工作簿对象。* @param excelModelDto Excel模型数据传输对象。* @return 第一行标题单元格样式。*/private CellStyle buildFirstTitleCellStyle(Workbook workbook, ExcelModelDto excelModelDto) {// 根据字段类型来获取背景色short backGroundColor = excelModelDto.getBackgroundColor();CellStyle cellStyle = this.getCellStyle(workbook, backGroundColor);Font font = this.getFontStyle(workbook, Font.COLOR_NORMAL, true);cellStyle.setFont(font);return cellStyle;}/*** 在指定的工作簿和工作表中追加枚举类型的项,并设置公式引用。** @param workbook 工作簿对象* @param sheet 工作表对象* @param childCellStyle 子单元格样式* @param secondTitleName 第二级标题名称* @param items 枚举类型的项数组* @param index 当前项在总体中的索引位置*/private void appendItems(Workbook workbook, Sheet sheet, CellStyle childCellStyle, String secondTitleName, String[] items, int index) {// 如果有序列单元格,则创建一个sheet页,来保存所有的枚举类型,同时隐藏该sheet页Sheet itemsSheet = this.getSheetByName(workbook, ITEM_SHEET_NAME, true);// 追加sheet的时候,需要看隐藏sheet的列已经到哪一列了,避免追加时将原有隐藏列覆盖掉int existItemCell = this.getLastCellNum(itemsSheet, 0);// 将枚举数组写入到独立的sheet页中,同时设置表头格式String formula = this.writeItems(itemsSheet, childCellStyle, secondTitleName, existItemCell, items);// 设置公式到模板的sheet页中,格式化后的最终公式为// =itemSheet!$B$1:$B$88// 表明该单元格引用的是 itemSheet sheet页中 B1~B88的数据formula = String.format("=%s!%s", ITEM_SHEET_NAME, formula);this.setItems(sheet, formula, FIRST_ROW, LAST_ROW, index, index);}/*** 获取单元格样式对象** @param workbook 工作簿* @return 返回样式对象*/public CellStyle getContentCellStyle(Workbook workbook) {CellStyle cellStyle = workbook.createCellStyle();CreationHelper createHelper = workbook.getCreationHelper();cellStyle.setDataFormat(createHelper.createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss"));// 背景色为纯色cellStyle.setFillPattern(FillPatternType.NO_FILL);// 设置单元格格式为文本格式DataFormat format = workbook.createDataFormat();cellStyle.setDataFormat(format.getFormat("@"));return cellStyle;}}
相关文章:
POI创建Excel文件
文章目录 1、背景2、创建表格2.1 定义表头对象2.2 Excel生成器2.3 创建模板2.4 处理Excel表头2.5 处理Excel内容单元格样式2.6 处理单个表头 3、追加sheet4、静态工具5、单元测试6、完整代码示例 1、背景 需求中有需要用户自定义Excel表格表头,然后生成Excel文件&a…...
CentOS虚拟机固定ip以及出现的问题
1.打开终端,进入网卡配置目录: cd etc/sysconfig/network-scripts 2.找到网卡配置文件,我这里是 ifcfg-ens32(替换成你自己的文件) 4.进入ifcfg-ens32,注释IPV6,修改别的参数如下图 TYPEEther…...
【Python】常用命令提示符
Python常用的命令提示符 一、Python环境基础命令【Windows】 于Windows环境下,针对Python,在CMD(命令提示符)常用的命令以及具体用法,怎么用; 主要包含:运行脚本、包管理、虚拟环境、调试与…...
Java引用RabbitMQ快速入门
这里写目录 Java发送消息给MQ消费者接收消息实现一个队列绑定多个消费者消息推送限制 Fanout交换机路由的作用Direct交换机使用案例 Java发送消息给MQ public void testSendMessage() throws IOException, TimeoutException {// 1.建立连接ConnectionFactory factory new Conn…...
USB接口的PCB设计
目录 USB接口简介 USB3.0接口 USB接口的电路设计 USB接口的PCB设计 USB接口简介 USB(通用串行总线)接口是一种广泛应用于电子设备的标准连接技术,自1996年由英特尔、微软等公司联合推出以来,逐步取代了传统串口、并口等复杂接…...
星纪魅族新品发布会定档5月13日,Note 16系列战神归来
5 月 13 日,星纪魅族将举办 Note 16 系列新品线上发布会。届时,国民严选魅族 Note 16 系列将战神归来,刷新用户对“高性价比科技”的想象,开启一场关乎「国民 AI 科技平权」的革新盛宴。 无创意不魅族,花式创意邀请即日…...
Jenkins+Newman实现接口自动化测试
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 一、是什么Newman Newman就是纽曼手机这个经典牌子,哈哈,开玩笑啦。。。别当真,简单地说Newman就是命令行版的Postman&…...
window 显示驱动开发-线程和同步级别为零级
在零级线程处理和同步中,WDDM 允许以可重入的方式对显示微型端口驱动程序进行零级 DxgkDdi*Xxx 调用。 也就是说,多个线程可以通过调用零级 DDI 同时进入驱动程序。 驱动程序应预期系统中的任何线程会传入,并应相应地保护该线程的数据。 尽…...
RabbitMQ ①-MQ | Linux安装RabbitMQ | 快速上手
MQ MQ(Message Queue)即消息队列,是一种应用间通信的一种方式。消息队列是一种异步通信方式,生产者(Producer)将消息放入队列,消费者(Consumer)从队列中取出消息进行消费…...
tinyrenderer笔记(Shadow Mapping)
tinyrenderer个人代码仓库:tinyrenderer个人练习代码 前言 阴影是光线被阻挡的结果;当光源的光线由于其他物体的阻挡而无法到达物体表面时,该物体就会产生阴影。阴影能使场景看起来更真实,并让观察者获得物体之间的空间位置关系。…...
【quantity】1 SI Prefixes 实现解析(prefix.rs)
一、源码 // prefix.rs //! SI Prefixes (国际单位制词头) //! //! 提供所有标准SI词头用于单位转换,仅处理10的幂次 //! //! Provides all standard SI prefixes for unit conversion, handling only powers of 10.use typenum::{Z0, P1, P2, P3, P6, P9, P12, …...
如何开发一个笑话管理小工具
前言 笔者曾经开发过一个可以对笑话浏览、收藏、分类、编辑上传的小工具(笔者开发后台,另外一个朋友负责小程序前台开发),如今所租用的服务器到期了,特此记录一下。 数据层 部署数据库 # 拉取Mysql镜像 docker pull…...
Transformer-LSTM混合模型在时序回归中的完整流程研究
Transformer-LSTM混合模型在时序回归中的完整流程研究 引言与背景 深度学习中的长期依赖建模一直是时序预测的核心问题。长短期记忆网络(LSTM)作为一种循环神经网络,因其特殊的门控结构能够有效捕捉序列的历史信息,并在时序预测…...
深入浅出iOS性能优化:打造极致用户体验的实战指南
前言 在当今移动应用竞争激烈的时代,性能优化已经成为iOS开发中不可或缺的重要环节。一个性能优秀的应用不仅能给用户带来流畅的使用体验,还能减少设备资源消耗,延长电池寿命,提高用户留存率。本文将深入探讨iOS性能优化的各个方…...
Spring AI 与大语言模型工具调用机制详细笔记
一、基本概念 大语言模型(LLM)工具调用机制是一种允许AI模型与外部系统交互的技术框架,它使模型能够在对话过程中请求调用预定义的函数或服务。这种机制极大地扩展了大模型的能力边界,使其不再局限于静态知识,而是能够…...
数据清洗-电商双11美妆数据分析
1.数据读取(前八行) 2.数据清洗 2.1 因为数据中存在重复跟空值,将数据进行重复值处理 (删除重复值) 2.2 缺失值处理 存在的缺失值很可能意味着售出的数量为0或者评论的数量为0,所以我们用0来填补缺失值 2…...
公司项目架构搭建者
公司项目架构搭建者分析 项目架构搭建的核心角色 #mermaid-svg-FzOOhBwW3tctx2AR {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-FzOOhBwW3tctx2AR .error-icon{fill:#552222;}#mermaid-svg-FzOOhBwW3tctx2AR .err…...
广告场景下的检索平台技术
检索方向概述 数据检索领域技术选型大体分为SQL事务数据库、NoSQL数据库、分析型数据库三个类型。 SQL数据库的设计思路是采用关系模型组织数据,注重读写操作的一致性,注重数据的绝对安全。为了实现这一思路,SQL数据库往往会牺牲部分性能&…...
LintCode407-加一,LintCode第479题-数组第二大数
第407题: 描述 给定一个非负数,表示一个数字数组,在该数的基础上1,返回一个新的数组。 该数字按照数位高低进行排列,最高位的数在列表的最前面. 样例 1: 输入:[1,2,3] 输出:[1,2,4] 样例 …...
网络安全的范式革命:从被动防御到 AI 驱动的主动对抗
当黑客利用生成式 AI 在 30 秒内生成 10 万组钓鱼邮件,当恶意代码学会根据网络环境自主进化,传统网络安全防线正面临前所未有的挑战。2025 年,全球网络安全领域正在经历一场从 “被动挨打” 到 “主动出击” 的革命性转变,AI 与量…...
内网im软件,支持企业云盘的协同办公软件推荐
BeeWorks不仅是一个即时通讯工具,更是一个综合性的企业管理平台。其云盘功能支持大容量文件存储,便企业集中管理文件。并且具备在线协同编辑的能力,这使得企业在文件管理和团队协作方面更加高效和便捷。以下是BeeWorks在企业云盘和在线协同编…...
JAVA SE(9)——多态
1.多态的概念&作用 多态(Polymorphism)是面向对象编程的三大基本特性之一(封装和继承已经讲过了),它允许不同类的对象对同一消息做出不同的响应。具体来说,多态允许基类/父类的引用指向派生类/子类的对象(向上转型…...
单调栈算法精解(Java实现):从原理到高频面试题
在算法与数据结构的领域中,单调栈(Monotonic Stack)凭借其独特的设计和高效的求解能力,成为解决特定类型问题的神兵利器。它通过维护栈内元素的单调性,能将许多问题的时间复杂度从暴力解法的\(O(n)\)优化至\(O(n)\)&am…...
密码工具类-生成随机密码校验密码强度是否满足要求
生成随机密码 符合密码强度的密码要求: 至少有一个大写字母至少有一个小写字母至少有一个数字至少有一个特殊字符长度满足要求(通常为8-16位) // 大写字母private static final String UPPERCASE "ABCDEFGHIJKLMNOPQRSTUVWXYZ";…...
什么是进程,如何管理进程
基本概念(什么是进程?) 课本概念:程序的一个执行实例,正在执行的程序等内核观点:担当分配系统资源(CPU时间,内存)的实体。 描述进程-PCB 进程信息被放在一个叫做进程控…...
小刚说C语言刷题—1044 -找出最经济型的包装箱型号
1.题目描述 已知有 A,B,C,D,E五种包装箱,为了不浪费材料,小于 10公斤的用 A型,大于等于 10公斤小于 20 公斤的用 B型,大于等于 20公斤小于 40 公斤的用 C型,大于等于 40…...
用 GRPO 魔法点亮Text2SQL 的推理之路:让模型“思考”得更像人类
推理能力(Chain of Thought, CoT)可以帮助模型逐步解释其思考过程,从而提高Text-to-SQL 生成的准确性和可解释性。本文探讨了如何将一个标准的 7B 参数的大型语言模型(Qwen2.5-Coder-7B-Instruct)转变为一个能够为Text…...
k8s service的类型
service和Pods service通过使用labels指向pods,而不是指向deployments或者replicasets。这种设计的灵活性极高,因为创建pods的方式有很多,而Service不需要关心pods通过那种方式创建 不使用service(首先看不使用service的情况) 如下…...
机器学习 day6 -线性回归练习
题目: 从Kaggle的“House Prices - Advanced Regression Techniques”数据集使用Pandas读取数据,并查看数据的基本信息。选择一些你认为对房屋价格有重要影响的特征,并进行数据预处理(如缺失值处理、异常值处理等)。…...
机器学习-简要与数据集加载
一.机器学习简要 1.1 概念 机器学习即计算机在数据中总结规律并预测未来结果,这一过程仿照人类的学习过程进行。 深度学习是机器学习中的重要算法的其中之一,是一种偏近现代的算法。 1.2 机器学习发展历史 从上世纪50年代的图灵测试提出、塞缪尔开发…...
HTTP请求与前端资源未优化的系统性风险与高性能优化方案
目录 前言一、未合并静态资源:HTTP请求的隐形杀手1.1 多文件拆分的代价1.2 合并策略与工具链实践 二、未启用GZIP压缩:传输流量的浪费2.1 文本资源的压缩潜力2.2 服务端配置与压缩算法选择 三、未配置浏览器缓存:重复请求的根源3.1 缓存失效的…...
黑马点评day04(分布式锁-setnx)
4、分布式锁 4.1 、基本原理和实现方式对比 分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程并行&#x…...
哈尔滨服务器租用
选择一家正规的本地服务商,能够直接促进您网站今后的发展、确保您企业的信息化进程安全、高效。擦亮您的慧眼,用我的经验告诉您该怎么选择服务商。。。。。。。。综合我们为数据客户服务的经验,选择服务器租用、服务提供商客户所需要关注的主…...
企业级RAG架构设计:从FAISS索引到HyDE优化的全链路拆解,金融/医疗领域RAG落地案例与避坑指南(附架构图)
本文较长,纯干货,建议点赞收藏,以免遗失。更多AI大模型应用开发学习内容,尽在聚客AI学院。 一. RAG技术概述 1.1 什么是RAG? RAG(Retrieval-Augmented Generation,检索增强生成) 是…...
js获取uniapp获取webview内容高度
js获取uniapp获取webview内容高度 在uni-app中,如果你想要获取webview的内容高度,可以使用uni-app提供的bindload事件来监听webview的加载,然后通过调用webview的invokeMethod方法来获取内容的高度。 以下是一个示例代码: <te…...
AI量化解析:从暴跌5%到飙涨3%—非线性动力学模型重构黄金极端波动预测框架
AI分析:假期效应褪去,金价回调背后的市场逻辑 五一假期期间,全球贵金属市场经历显著波动。5月1日,现货黄金单日跌幅达5.06%,价格从历史高位回落至3200美元/盎司附近,国内金饰价格同步回调,主流…...
Python之pip图形化(GUI界面)辅助管理工具
Python之pip图形化(GUI界面)辅助管理工具 pip 是 Python 的包管理工具,用于安装、管理、更新和卸载 Python 包(模块)。用于第三方库的安装和管理过程,是 Python 开发中不可或缺的工具。 包的安装、更新、…...
数字传播生态中开源链动模式与智能技术协同驱动的品牌认知重构研究——基于“开源链动2+1模式+AI智能名片+S2B2C商城小程序”的场景化传播实践
摘要:在数字传播碎片化与用户注意力稀缺的双重挑战下,传统品牌认知构建模式面临效率衰减与情感黏性缺失的困境。本文以“开源链动21模式AI智能名片S2B2C商城小程序”的协同创新为切入点,构建“技术赋能-场景重构-认知强化”的分析框架。通过对…...
小芯片大战略:Chiplet技术如何重构全球半导体竞争格局?
在科技飞速发展的今天,半导体行业作为信息技术的核心领域之一,其发展速度和创新水平对全球经济的发展具有举足轻重的影响。然而,随着芯片制造工艺的不断进步,传统的单片集成方式逐渐遇到了技术瓶颈,如摩尔定律逐渐逼近…...
链表的面试题3找出中间节点
来来来,接着继续我们的第三道题 。 解法 暴力求解 快慢指针 https://leetcode.cn/problems/middle-of-the-linked-list/submissions/ 这道题的话,思路是非常明确的,就是让你找出我们这个所谓的中间节点并且输出。 那这道题我们就需要注意…...
Java泛型深度解析与电商场景应用
学海无涯,志当存远。燃心砺志,奋进不辍。 愿诸君得此鸡汤,如沐春风,事业有成。 若觉此言甚善,烦请赐赞一枚,共励学途,同铸辉煌! 泛型的工作原理可能包括类型擦除、参数化类型、类型边…...
C语言 指针(7)
目录 1.函数指针变量 2.函数指针数组 3.转移表 1.函数指针变量 1.1函数指针变量的创建 什么是函数指针变量呢? 根据前面学习整型指针,数组指针的时候,我们的类比关系,我们不难得出结论: 函数指针变量应该是用来…...
go 编译报错:build constraints exclude all Go files
报错信息: package command-line-arguments imports github.com/amikos-tech/chroma-go imports github.com/amikos-tech/chroma-go/pkg/embeddings/default_ef imports github.com/amikos-tech/chroma-go/pkg/tokenizers/libtokenizers: …...
Android Service 从 1.0 到 16 的演进史
一、Android 1.0(API 1) - 服务的诞生 核心特性: 基础服务组件:作为四大组件之一,Service 用于在后台执行长时间运行的任务,不提供 UI 界面。 启动方式:通过 startService() 启动独立运行的服…...
如何保障服务器租用中的数据安全?
网络科技和互联网的飞速发展,让用户越来越依赖与网络业务,各个行业开展了不同的线上服务,租用服务器已经成为必不可少的组成部分,能够为企业带来便捷,但是数据安全也是不可忽视的,为了能够保护服务器中数据…...
python校园二手交易管理系统-闲置物品交易系统
目录 技术栈介绍具体实现截图系统设计研究方法:设计步骤设计流程核心代码部分展示研究方法详细视频演示试验方案论文大纲源码获取/详细视频演示 技术栈介绍 Django-SpringBoot-php-Node.js-flask 本课题的研究方法和研究步骤基本合理,难度适中…...
消除AttributeError: module ‘ttsfrd‘ has no attribute ‘TtsFrontendEngine‘报错输出的记录
#工作记录 尝试消除 消除“模块ttsfrd没有属性ttsfrontendengine”的错误的记录 报错摘录: Traceback (most recent call last): File "F:\PythonProjects\CosyVoice\webui.py", line 188, in <module> cosyvoice CosyVoice(args.model_di…...
MD2card + Deepseek 王炸组合 一键制作小红书知识卡片
本文目录 MD2Card介绍使用示例deepseek 提示词输出结果MD2Card 制作小红书卡片 MD2Card介绍 MD2Card 是一个免费的 Markdown 转知识卡片工具,支持一键生成小红书风格海报、社交媒体文案排版,让创作者轻松制作精美的图文内容。支持多种主题风格、长文自动…...
Relay算子注册(在pytorch.py端调用)
1. Relay算子注册 (C层) (a) 算子属性注册 路径: src/relay/op/nn/nn.cc RELAY_REGISTER_OP("hardswish").set_num_inputs(1).add_argument("data", "Tensor", "Input tensor.").set_support_level(3).add_type_rel("Identity…...
基于RT-Thread的STM32F4开发第二讲第一篇——ADC
文章目录 前言一、RT-Thread工程创建二、ADC工程创建三、ADC功能实现1.ADC.c2.ADC.h3.mian.c 四、效果展示和工程分享总结 前言 ADC是什么不多讲了,前面裸机操作部分有很多讲述。我要说的是RT-Thread对STM32的ADC外设的适配极其不好,特别是STM32G4系类&…...