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

Alibaba EasyExcel 导入导出全家桶

一、阿里巴巴EasyExcel的优势

        首先说下EasyExcel相对 Apache poi的优势:

        EasyExcel也是阿里研发在poi基础上做了封装,改进产物。它替开发者做了注解列表解析,表格填充等一系列代码编写工作,并将此抽象成通用和可扩展的框架。相对poi,在数据量比较大的时候,它有着更优越的性能体现。导出的时候,easyexcel使用优化的反射技术,避免poi频繁的去创建cell和row对象;导入的时候,它的解析器AnalysisEventListener,可设置批量阈值 BATCH_COUNT,达到阈值就往数据库插入数据,然后清空解析器内部缓存,相同的表格,easyexcel导入所占用的内存要比poi节省90%,避免了大数据量导入的时候,造成的内存占用井喷(这使得stop the world的时间可能会被集中,而系统可能会出现短暂的停摆。),而GC不能均衡调动垃圾回收。同时也避免堆积数据后,sql的巨量数据的批量插入,导致超出mybatis批量插入语句能承受的最大长度限制。

二、EasyExcel核心util类

@Slf4j
public class EasyExcels {public static final String EXT_NAME_XLSX = "xlsx";public static final String EXT_NAME_XLS = "xls";/**** @param response* @param data* @param filename* @param sheetName* @param selectMap  自定义下拉列,但是既然数据都导出了,下拉用处何在?这个需求比较少* @param <T>* @throws IOException*/public static <T> void write(HttpServletResponse response, List<T> data, String filename, String sheetName,List<KeyValue<ExcelColumn, List<String>>> selectMap) throws IOException {setResponse(response, filename);if (StringUtils.isBlank(sheetName)) {sheetName = filename;}// 输出 Exceltry {EasyExcel.write(response.getOutputStream(), data != null && !data.isEmpty() ? data.get(0).getClass() : null).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度.registerWriteHandler(new CustomCellWriteWeightConfig()) // Excel 列宽自适应.registerWriteHandler(EasyExcelStyle.horizontalCellStyleStrategy) //内容样式.registerWriteHandler(new SelectWriteHandler(selectMap)) // 基于固定 sheet 实现下拉框.sheet(sheetName).doWrite(data);} catch (Exception e) {e.printStackTrace();} finally {response.getOutputStream().close();}}// 简单导入读取,不做解析,不做校验public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException {return EasyExcel.read(file.getInputStream(), head, null).autoCloseStream(false)  // 不要自动关闭,交给 Servlet 自己处理.doReadAllSync();}// 需要配合监听器解析数据public static <T> void read(MultipartFile file, Class<T> head, ReadListener<T> listener) throws IOException {EasyExcel.read(file.getInputStream(), head, listener).sheet().doRead();}// 不带下拉列的导出,用的比较多public static <T> void export(HttpServletResponse response, List<T> data, String filename, String sheetName) throws IOException {setResponse(response, filename);if (StringUtils.isBlank(sheetName)) {sheetName = filename;}EasyExcel.write(response.getOutputStream(), data != null && !data.isEmpty() ? data.get(0).getClass() : null).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).registerWriteHandler(new CustomCellWriteWeightConfig()).registerWriteHandler(EasyExcelStyle.horizontalCellStyleStrategy).sheet(sheetName).doWrite(data);}// 用于合并单元格列的导出public static <T> void export(HttpServletResponse response, List<T> data, String filename, String sheetName, RowWriteHandler handler) throws IOException {setResponse(response, filename);EasyExcel.write(response.getOutputStream(), data != null && !data.isEmpty() ? data.get(0).getClass() : null).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).registerWriteHandler(new CustomCellWriteWeightConfig()).registerWriteHandler(EasyExcelStyle.horizontalCellStyleStrategy).registerWriteHandler(handler).sheet(sheetName).doWrite(data);}// 用于导出表头模板,填充导入数据用的excel模板,因为是模板,所以肯定会有下拉列的需求public static <T> void export(HttpServletResponse response, Class<T> clazz, String filename) throws IOException {setResponse(response, filename);Map<Integer, ExcelSelectedResolve> selectedMap = resolveSelectedAnnotation(clazz);EasyExcel.write(response.getOutputStream(), clazz).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
//                .registerWriteHandler(new CustomCellWriteHeightConfig()).registerWriteHandler(new CustomCellWriteWeightConfig()).registerWriteHandler(EasyExcelStyle.horizontalCellStyleStrategy).registerWriteHandler(new SelectSheetWriteHandler(selectedMap)).sheet(filename).doWrite(Collections.emptyList());}/*** 解析表头类中的下拉注解* @param head 表头类* @param <T> 泛型* @return Map<下拉框列索引, 下拉框内容> map*/private static <T> Map<Integer, ExcelSelectedResolve> resolveSelectedAnnotation(Class<T> head) {Map<Integer, ExcelSelectedResolve> selectedMap = new HashMap<>();// getDeclaredFields(): 返回全部声明的属性;getFields(): 返回public类型的属性Field[] fields = head.getDeclaredFields();for (int i = 0; i < fields.length; i++){Field field = fields[i];// 解析注解信息ExcelSelected selected = field.getAnnotation(ExcelSelected.class);ExcelProperty property = field.getAnnotation(ExcelProperty.class);if (selected != null) {ExcelSelectedResolve excelSelectedResolve = new ExcelSelectedResolve();String[] source = excelSelectedResolve.resolveSelectedSource(selected);if (source != null && source.length > 0){excelSelectedResolve.setSource(source);excelSelectedResolve.setFirstRow(selected.firstRow());excelSelectedResolve.setLastRow(selected.lastRow());if (property != null && property.index() >= 0){selectedMap.put(property.index(), excelSelectedResolve);} else {selectedMap.put(i, excelSelectedResolve);}}}}return selectedMap;}public static void setResponse(HttpServletResponse response, String filename) throws IOException {setResponse(response, filename, EXT_NAME_XLSX);}public static void setResponse(HttpServletResponse response, String filename, String extName) throws IOException {String exportFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8.name());response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");response.setHeader("Access-Control-Expose-Headers", "token,Content-Type,Content-disposition");response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Connection, User-Agent, Cookie, token,Content-Type,Content-disposition");response.setHeader("Content-disposition", exportFilename + "." + extName);}}

三、导入解析监听器

那要使用easyexcel,首先要解决解析器抽象类的实现:

        当时第一次使用easyexcel的时候,对这个工具框架不熟悉,项目时间被催的紧,没时间去做设计,当时修改每个类的字段注解index属性,每个字段单独写校验语句,简直苦不堪言。我只想说,磨刀不误砍柴工,不注重设计的公司,只会被拖延更多的时间。

/*** @Title: ExcelImportReadListener* @Description: 其他人如果觉得invoke()方法不满足其需求,可以自己实现一下* @Author: wenrong* @Date: 2024/4/25 17:08* @Version:1.0*/
@Data
public abstract class ExcelImportReadListener<T extends ValidateBaseBo> extends AnalysisEventListener<T> {private static final Logger log = LoggerFactory.getLogger("excelReadListener");public static int BATCH_COUNT = 1000;private AtomicLong successNum = new AtomicLong();private final Class<T> clazz;private Validator<T> validator;private List<T> successData = new ArrayList<>();private List<T> failureData = new ArrayList<>();public ExcelImportReadListener(Class<T> clazz) {this.clazz = clazz;}@Overridepublic void invoke(T data, AnalysisContext context) {log.info("解析到一条数据:{}", JSONObject.toJSON(data));StringBuilder errMsg = new StringBuilder();try {//根据excel数据实体中的javax.validation + 正则表达式来校验excel数据errMsg.append(EasyExcelValidateHandler.validateEntity(data));// 额外自定义校验,以及设置数据属性的逻辑if (validator != null) {errMsg.append(validator.validate(data));}} catch (NoSuchFieldException e) {log.error(e.getMessage());}if (StringUtils.isNotEmpty(errMsg.toString())) {data.setErrMsg(errMsg.toString());failureData.add(data);} else {successData.add(data);successNum.incrementAndGet();}if (BATCH_COUNT != 0 && successData.size() >= BATCH_COUNT) {try {saveData();} catch (Exception e) {log.error(e.getMessage(), e);}successData.clear();}}@Overridepublic final void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {// 验证表头if (headMap.isEmpty()) {throw new ServiceException("无表头");}if (clazz != null) {try {Map<Integer, String> indexNameMap = getIndexNameMap(clazz);for (Integer index : indexNameMap.keySet()) {log.info("表头字段:{}", headMap.get(index));if (StringUtils.isEmpty(headMap.get(index))) {throw new ServiceException("未设置index");}// 对比excel表头和解析数据的java实体类的,看是否匹配if (!headMap.get(index).equals(indexNameMap.get(index))) {throw new ServiceException("导入模板错误");}}} catch (NoSuchFieldException e) {log.error(e.getMessage(), e);}}}@Overridepublic final void doAfterAllAnalysed(AnalysisContext context) {log.info("所有数据解析完成!共校验成功{}条数据,校验失败{}条数据", successNum.get(), failureData.size());try {saveData();} catch (Exception e) {log.error(e.getMessage(), e);}}/*** 将该类做成抽象类,在各service中实现saveDate方法,* 不侵入业务,同时不会让解析占用内存*/public void saveData() throws Exception {log.info("开始往数据库插入数据");}private Map<Integer, String> getIndexNameMap(Class<T> clazz) throws NoSuchFieldException {Map<Integer, String> excelPropertyMap = new HashMap<>();Field field;Field[] fields = clazz.getDeclaredFields();int sequence = 0;for (Field item : fields) {field = clazz.getDeclaredField(item.getName());field.setAccessible(true);ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);if (excelProperty != null) {// 避免每个列都要写index,插入或删除一个字段,所有的index都需要修改。默认为java实体类中字段的顺序。int index = excelProperty.index() == -1 ? sequence : excelProperty.index();String[] values = excelProperty.value();StringBuilder value = new StringBuilder();for (String v : values) {value.append(v);}excelPropertyMap.put(index, value.toString());sequence++;}}return excelPropertyMap;}}

3.1、解析成功的数据直接落库,错误数据导出

        ValidateBaseBo:用在导入的时候,将校验的错误保留下来,然后再把有问题的数据过滤出来,再导出,或者显示在前端的导入结果里,操作者可以按照错误信息把表格里的数据修改好后,再次导入,而且只将导入失败的数据导出,不用去原表中大片的数据中去找有错误信息的数据,目的是方便操作者快速定位表格里的问题数据。

/*** @Title: ValidateBaseBo* @Description:* @Author: wenrong* @Date: 2024/10/17 上午11:02* @Version:1.0*/
@Data
public abstract class ValidateBaseBo {@ExcelProperty(value = "错误信息")@TableField(exist = false)@ApiModelProperty(hidden = true)private String errMsg;
}

3.2、解析过程中校验数据正确性 

        除了javax.validation,基础的注解校验之外,如果还需要额外的校验,就自定义校验器作补充。

/*** @Title: ValidData* @Description: javax.validation 以外校验函数* @Author: wenrong* @Date: 2024/4/26 19:51* @Version:1.0*/
public interface Validator<T> {/*** 这里的实现方法,最后返回的如果为null,一定要返回"",否则会被转化为"null"** @param T t* @return ""*/String validate(T t);
}

3.3、导入/导出 Convertor 

        excel导入数据对应的实体类:要注意表格中的汉字和实际存入到数据库中数值的转换:Convertor

/*** @author wenrong* @date 2024-11-25 17:38:26*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("····")
public class YourClass extends ValidateBaseBo implements Serializable {private static final long serialVersionUID = 1L;public static Map<String, DyzProvinceSchool> provinceMap = new HashMap<>();public static Map<String, DyzProvinceSchool> schoolMap = new HashMap<>();@ExcelIgnore@TableId(type = IdType.AUTO)private Long id;@ApiModelProperty(value = "省份ID")@ExcelProperty(value = "省份", converter = ProvinceConvertor.class)@NotNull(message = "不能为空")private Integer provinceId;@ApiModelProperty(value = "学校ID")@ExcelProperty(value = "学校", converter = SchoolConvertor.class)@NotNull(message = "不能为空")private Integer schoolId;@ApiModelProperty(value = "节目代码")@ExcelProperty(value = "节目代码")@NotNull(message = "不能为空")private String worksNumber;@ApiModelProperty(value = "节目/项目名称")@ExcelProperty(value = "节目/项目名称")@NotNull(message = "不能为空")private String ``````;public String validate(Map<Integer, DyzProvinceSchool> provinceMap, Map<Integer, DyzProvinceSchool> schoolMap) {StringBuilder sb = new StringBuilder(this.getErrMsg() == null ? "" : this.getErrMsg());if (provinceMap.get(this.provinceId) == null) {sb.append("省份不存在: ").append(provinceId);}if (schoolMap.get(this.schoolId) == null) {sb.append("学校不存在: ").append(schoolId);}return sb.toString();}public static class GroupTypeConvertor implements Converter<Integer> {//导入的时候,将表格的汉字转换成java对应数据库的字段@Overridepublic Integer convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration) {switch (cellData.getStringValue()) {default:return 2;case "小学组":return 0;case "中学组":return 1;}}// 导出的时候,将数据库中存储的值,转换为用户能看懂的汉字@Overridepublic WriteCellData<?> convertToExcelData(Integer value,ExcelContentProperty excelContentProperty,GlobalConfiguration globalConfiguration) {switch (value) {default:return new WriteCellData<>("其他组");case 0:return new WriteCellData<>("小学组");case 1:return new WriteCellData<>("中学组");}}}public static class PresentConvertor implements Converter<Integer> {@Overridepublic Integer convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration) {if (cellData.getStringValue().equals("否")) {return 0;} else {return 1;}}@Overridepublic WriteCellData<?> convertToExcelData(Integer value,ExcelContentProperty excelContentProperty,GlobalConfiguration globalConfiguration) {switch (value) {default:return new WriteCellData<>("-");case 0:return new WriteCellData<>("否");case 1:return new WriteCellData<>("是");}}}public static class ProvinceConvertor implements Converter<Integer> {@Overridepublic Integer convertToJavaData(ReadCellData<?> cellData,ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration) throws Exception {if (provinceMap.isEmpty()) {throw new Exception("省份配置数据为空");}return StringUtils.isBlank(cellData.getStringValue()) ? null : provinceMap.get(cellData.getStringValue()).getId();}}public static class SchoolConvertor implements Converter<Integer> {@Overridepublic Integer convertToJavaData(ReadCellData<?> cellData,ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration) throws Exception {if (schoolMap.isEmpty()) {throw new Exception("学校配置数据为空");}return StringUtils.isBlank(cellData.getStringValue()) ? null : schoolMap.get(cellData.getStringValue()).getId();}// 导出转换省略掉}}

3.4、实现导入解析监听器    

        上面的解析监听器是个抽象类,是一种模板模式的设计思想应用,我们根据不同的业务,自己扩展invoke方法和saveData方法,但其实saveData也可以做成模板方法,只是需要依赖内部绑定一个数据层dao接口,Mapper,对于有的人来说,会耦合dao层,但我觉得如果dao层取一个接口,那么也没什么耦合的问题。节省不必要的重复代码,还是值得的。

那么上述的那个模板抽象解析监听器可以改为:

/*** @Title: ExcelImportReadListener* @Description: 其他人如果觉得invoke()方法不满足其需求,可以自己实现一下* @Author: wenrong* @Date: 2024/4/25 17:08* @Version:1.0*/
@Data
public abstract class ExcelImportReadListener<T extends ValidateBaseBo, S extends IService<T>> extends AnalysisEventListener<T> {private static final Logger log = LoggerFactory.getLogger("excelReadListener");public static int BATCH_COUNT = 1000;private AtomicLong successNum = new AtomicLong();private final Class<T> clazz;private S service;private Validator<T> validator;private List<T> successData = new ArrayList<>();private List<T> failureData = new ArrayList<>();public ExcelImportReadListener(Class<T> clazz, S service) {this.clazz = clazz;this.service = service;}@Overridepublic void invoke(T data, AnalysisContext context) {log.info("解析到一条数据:{}", JSONObject.toJSON(data));StringBuilder errMsg = new StringBuilder();try {//根据excel数据实体中的javax.validation + 正则表达式来校验excel数据errMsg.append(EasyExcelValidateHandler.validateEntity(data));// 额外自定义校验,以及设置数据属性的逻辑if (validator != null) {errMsg.append(validator.validate(data));}} catch (NoSuchFieldException e) {log.error(e.getMessage());}if (StringUtils.isNotEmpty(errMsg.toString())) {data.setErrMsg(errMsg.toString());failureData.add(data);} else {successData.add(data);successNum.incrementAndGet();}if (BATCH_COUNT != 0 && successData.size() >= BATCH_COUNT) {try {saveData();} catch (Exception e) {log.error(e.getMessage(), e);}successData.clear();}}@Overridepublic final void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {// 验证表头if (headMap.isEmpty()) {throw new ServiceException("无表头");}if (clazz != null) {try {Map<Integer, String> indexNameMap = getIndexNameMap(clazz);for (Integer index : indexNameMap.keySet()) {log.info("表头字段:{}", headMap.get(index));if (StringUtils.isEmpty(headMap.get(index))) {throw new ServiceException("未设置index");}if (!headMap.get(index).equals(indexNameMap.get(index))) {throw new ServiceException("导入模板错误");}}} catch (NoSuchFieldException e) {log.error(e.getMessage(), e);}}}@Overridepublic final void doAfterAllAnalysed(AnalysisContext context) {log.info("所有数据解析完成!共校验成功{}条数据,校验失败{}条数据", successNum.get(), failureData.size());try {saveData();} catch (Exception e) {log.error(e.getMessage(), e);}}/*** 将该类做成抽象类,在各service中实现saveDate方法,* 不侵入业务,同时不会让解析占用内存*/public void saveData() throws Exception {log.info("开始往数据库插入数据");List<T> successData = this.getSuccessData();List<T> failureData = this.getFailureData();boolean saved = service.saveBatch(successData);if (!saved) {successData.forEach(work -> work.setErrMsg("保存失败"));failureData.addAll(successData);} else {this.setSuccessData(successData);}}private Map<Integer, String> getIndexNameMap(Class<T> clazz) throws NoSuchFieldException {Map<Integer, String> excelPropertyMap = new HashMap<>();Field field;Field[] fields = clazz.getDeclaredFields();int sequence = 0;for (Field item : fields) {field = clazz.getDeclaredField(item.getName());field.setAccessible(true);ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);if (excelProperty != null) {int index = excelProperty.index() == -1 ? sequence : excelProperty.index();String[] values = excelProperty.value();StringBuilder value = new StringBuilder();for (String v : values) {value.append(v);}excelPropertyMap.put(index, value.toString());sequence++;}}return excelPropertyMap;}}

业务代码中实现模板解析监听器的代码示例:

    @Override@Transactionalpublic ExcelImportReadListener<BasicWorks> importExcel(MultipartFile file) throws IOException {List<DyzProvinceSchool> schoolList = dyzProvinceSchoolService.getSchoolList();List<DyzProvinceSchool> provinceList = dyzProvinceSchoolService.getProvinceList();Map<String, DyzProvinceSchool> schoolMap = schoolList.stream().collect(Collectors.toMap(DyzProvinceSchool::getSchoolName, s -> s));Map<Integer, DyzProvinceSchool> schoolMap1 = schoolList.stream().collect(Collectors.toMap(DyzProvinceSchool::getId, s -> s));Map<String, DyzProvinceSchool> provinceMap = provinceList.stream().collect(Collectors.toMap(DyzProvinceSchool::getProvinceName, s -> s));Map<Integer, DyzProvinceSchool> provinceMap1 = provinceList.stream().collect(Collectors.toMap(DyzProvinceSchool::getId, s -> s));BasicWorks.schoolMap = schoolMap;BasicWorks.provinceMap = provinceMap;// 匿名内部类扩展模板监听器ExcelImportReadListener<BasicWorks> readListener = new ExcelImportReadListener<BasicWorks>(BasicWorks.class) {@Overridepublic void invoke(BasicWorks data, AnalysisContext context) {Set<String> allDataExistInExcel = new HashSet<>();Set<String> allDataExistInDataSource = list().stream().map(BasicWorks::getWorksNumber).collect(Collectors.toSet());List<BasicWorks> failureData = this.getFailureData();StringBuilder errMsg = new StringBuilder(data.getErrMsg() == null ? "" : data.getErrMsg());if (StringUtils.isBlank(data.getWorksNumber())) {errMsg.append("节目代码不能为空,");data.setErrMsg(errMsg.toString());failureData.add(data);} else {if (allDataExistInExcel.contains(data.getWorksNumber())) {errMsg.append("Excel表格中存在重复的数据,").append("节目代码:").append(data.getWorksNumber());data.setErrMsg(errMsg.toString());failureData.add(data);allDataExistInExcel.add(data.getWorksNumber());}if (allDataExistInDataSource.contains(data.getWorksNumber())) {errMsg.append("数据库中存在重复的数据,").append("节目代码:").append(data.getWorksNumber());data.setErrMsg(errMsg.toString());failureData.add(data);allDataExistInExcel.add(data.getWorksNumber());}}allDataExistInExcel.add(data.getWorksNumber());super.invoke(data, context);}// 设置javax.validation以外校验器,将会在invoke方法里执行校验readListener.setValidator(work -> work.validate(provinceMap1, schoolMap1));// 导入 ExcelEasyExcels.read(file, BasicWorks.class, readListener);return readListener;}

另外还有需要将表格中图片导入后自动上传到文件服务,然后将url保存在数据库的需求:

public ExcelImportReadListener<BasicHotel> importExcel(MultipartFile file) throws IOException {//获取图片,联合Apache 的ExcelUtil,ExcelPicUtil工具类,获取图片数据对象PictureDataExcelReader reader = ExcelUtil.getReader(file.getInputStream());Map<String, PictureData> picMap = ExcelPicUtil.getPicMap(reader.getWorkbook(), 0);ExcelImportReadListener<BasicHotel> readListener = new ExcelImportReadListener<BasicHotel>(BasicHotel.class) {@Overridepublic void invoke(BasicHotel data, AnalysisContext context) {Set<String> allDataExistInDataSource = list().stream().map(BasicHotel::getHotelName).collect(Collectors.toSet());Set<String> allDataExistInExcel = new HashSet<>();List<BasicHotel> failureData = this.getFailureData();StringBuilder errMsg = new StringBuilder(data.getErrMsg() == null ? "" : data.getErrMsg());if (StringUtils.isEmpty(data.getErrMsg())) {errMsg.append("酒店名称不能为空,");data.setErrMsg(errMsg.toString());failureData.add(data);} else {if (allDataExistInExcel.contains(data.getHotelName())) {errMsg.append("Excel表格中存在重复的数据,").append("酒店名称:").append(data.getHotelName());data.setErrMsg(errMsg.toString());failureData.add(data);allDataExistInExcel.add(data.getHotelName());}if (allDataExistInDataSource.contains(data.getHotelName())) {errMsg.append("数据库中存在重复的数据,").append("酒店名称:").append(data.getHotelName());data.setErrMsg(errMsg.toString());failureData.add(data);allDataExistInExcel.add(data.getHotelName());}}allDataExistInExcel.add(data.getHotelName());String err = "";int rowIndex = context.readRowHolder().getRowIndex() + 1;PictureData pictureData = picMap.get(rowIndex + "_0");if (pictureData == null) {err = String.format(data.getErrMsg() + "第%s行,%s", rowIndex, "酒店照片为空");}try {// 上传图片String fileUrl = ossFileController.ftpUploadFile(pictureData.getData(), "", data.getHotelName());data.setPicture(fileUrl);} catch (IOException ex) {err = String.format(data.getErrMsg() + "第%s行,%s", rowIndex, "酒店照片为空上传失败");}data.setErrMsg(err);super.invoke(data, context);}};// 导入 ExcelEasyExcels.read(file, BasicHotel.class, readListener);return readListener;
}

        这是导入部分,导出部分,五花八门的需求就比较多了。

四、导出

4.1、导出 数据导入模板

模板一般会有下拉选项列的需求,下拉列一般用注解枚举几个就行了:


import java.lang.annotation.*;/*** 标注导出的列为下拉框类型,并为下拉框设置内容*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelSelected {/*** 固定下拉内容*/String[] source() default {};/*** 设置下拉框的起始行,默认为第二行*/int firstRow() default 1;/*** 设置下拉框的结束行,默认为最后一行*/int lastRow() default 0x10000;
}

下拉注解解析器:

@Data
@Slf4j
public class ExcelSelectedResolve {/*** 下拉内容*/private String[] source;/*** 设置下拉框的起始行,默认为第二行*/private int firstRow;/*** 设置下拉框的结束行,默认为最后一行*/private int lastRow;public String[] resolveSelectedSource(ExcelSelected excelSelected) {if (excelSelected == null) {return null;}// 获取固定下拉框的内容String[] source = excelSelected.source();if (source.length > 0) {return source;}//        // 获取动态下拉框的内容
//        Class<? extends ExcelDynamicSelect>[] classes = excelSelected.sourceClass();
//        if (classes.length > 0) {
//            try {
//                ExcelDynamicSelect excelDynamicSelect = classes[0].newInstance();
//                String[] dynamicSelectSource = excelDynamicSelect.getSource();
//                if (dynamicSelectSource != null && dynamicSelectSource.length > 0) {
//                    return dynamicSelectSource;
//                }
//            } catch (InstantiationException | IllegalAccessException e) {
//                log.error("解析动态下拉框数据异常", e);
//            }
//        }return null;}}

下拉handler:

public class SelectSheetWriteHandler implements SheetWriteHandler {private final Map<Integer, ExcelSelectedResolve> selectedMap;public SelectSheetWriteHandler(Map<Integer, ExcelSelectedResolve> selectedMap) {this.selectedMap = selectedMap;}/*** Called before create the sheet*/@Overridepublic void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {}/*** Called after the sheet is created*/@Overridepublic void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {// 这里可以对cell进行任何操作Sheet sheet = writeSheetHolder.getSheet();DataValidationHelper helper = sheet.getDataValidationHelper();selectedMap.forEach((k, v) -> {// 设置下拉列表的行: 首行,末行,首列,末列CellRangeAddressList rangeList = new CellRangeAddressList(v.getFirstRow(), v.getLastRow(), k, k);// 设置下拉列表的值DataValidationConstraint constraint = helper.createExplicitListConstraint(v.getSource());// 设置约束DataValidation validation = helper.createValidation(constraint, rangeList);// 阻止输入非下拉选项的值validation.setErrorStyle(DataValidation.ErrorStyle.STOP);validation.setShowErrorBox(true);validation.setSuppressDropDownArrow(true);validation.createErrorBox("提示", "请输入下拉选项中的内容");sheet.addValidationData(validation);});}}

        其实如果是动态的下拉列表,不能固定枚举的话,直接从配置数据表中拉出业务配置列表,将列表作为传参,使用util类EasyExcels第一个方法就好。

    @ExcelProperty(value = "组别")@ExcelSelected(source = {"小学组", "初中组"})@ApiModelProperty(value = "组别:0-小学组,1-初中组,2-其他组")@NotNull(message = "不能为空")private String groupType;@ExcelProperty(value = "是否出席")@ExcelSelected(source = {"是", "否"})@ApiModelProperty(value = "是否出席:0-否,1是")@NotNull(message = "不能为空")private String present;

4.2、图片导出convertor:

public class UrlPictureConverter implements Converter<String> {public static int urlConnectTimeout = 2000;public static int urlReadTimeout = 6000;@Overridepublic Class<?> supportJavaTypeKey() {return String.class;}@Overridepublic WriteCellData<?> convertToExcelData(String url, ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration) throws IOException {InputStream inputStream = null;try {URL value = new URL(url);if (ObjectUtils.isEmpty(value)) {return new WriteCellData<>("");}URLConnection urlConnection = value.openConnection();urlConnection.setConnectTimeout(urlConnectTimeout);urlConnection.setReadTimeout(urlReadTimeout);inputStream = urlConnection.getInputStream();byte[] bytes = IoUtils.toByteArray(inputStream);return new WriteCellData<>(bytes);} catch (Exception e) {log.info("图片获取异常", e);return new WriteCellData<>("图片获取异常");} finally {if (inputStream != null) {inputStream.close();}}}
}

4.3、有合并单元格导出:

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ExcelIgnoreUnannotated
@Slf4j
public class WorkJudgesStatisticsVo implements Serializable {private static final long serialVersionUID = 1L;@ExcelProperty(value = "序号", index = 0)private String sequence;public static class MergeStrategy implements RowWriteHandler {private int totalRowNum;public MergeStrategy(int totalRowNum) {this.totalRowNum = totalRowNum;}public static MergeStrategy build(int totalRowNum) {return new MergeStrategy(totalRowNum);}@Overridepublic void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {if (isHead) {// 处理表头return;}log.info("当前行号:{}", row.getRowNum());log.info("总行数:{}", totalRowNum);// 合并if (row.getRowNum() != totalRowNum + 1) {return;}writeSheetHolder.getSheet().addMergedRegion(new CellRangeAddress(writeSheetHolder.getLastRowIndex(), writeSheetHolder.getLastRowIndex(), 5, 6));}}}

4.4、行转列,并使用模板的方式导出:

@Override
public void selectWorksJudgesResultReview(HttpServletResponse response) throws IOException {List<Map<String, Object>> views = scoreReviewWorksJudgesMapper.selectWorksJudgesResultReview();List<DyzScoreReviewWorksJudges> reviewWorksJudges = scoreReviewWorksJudgesMapper.selectList();List<DyzScoreWorksFiles> scoreWorksFiles = worksFilesMapper.selectList();HashMap<Long, List<DyzScoreWorksFiles>> fileMap = new HashMap<>();scoreWorksFiles.forEach(f -> {List<DyzScoreWorksFiles> files = fileMap.computeIfAbsent(f.getWorksId(), k -> new ArrayList<>());files.add(f);});HashMap<Long, List<DyzScoreReviewWorksJudges>> scoreMap = new HashMap<>();reviewWorksJudges.forEach(judge -> {List<DyzScoreReviewWorksJudges> judges = scoreMap.computeIfAbsent(judge.getWorksId(), k -> new ArrayList<>());judges.add(judge);});AtomicInteger sequence = new AtomicInteger(0);views.forEach(map -> {map.put("sequence", String.valueOf(sequence.incrementAndGet()));Long workId = Long.valueOf(map.get("workId").toString());List<DyzScoreReviewWorksJudges> judges = scoreMap.get(workId);for (int i = 0; i < 15; i++) {map.put("score" + (i + 1), "");map.put("correctness" + (i + 1), "");}map.put("avgScore", "");map.put("avgScore1", "");if (judges != null && judges.size() > 0) {AtomicInteger serialNo = new AtomicInteger(0);AtomicInteger serialNo1 = new AtomicInteger(0);judges.forEach(j -> {map.put("score" + serialNo.incrementAndGet(), j.getScore());map.put("correctness" + serialNo1.incrementAndGet(), j.getRemark());});judges.sort(Comparator.comparing(DyzScoreReviewWorksJudges::getScore));BigDecimal sum = judges.stream().map(DyzScoreReviewWorksJudges::getScore).reduce(BigDecimal.ZERO, BigDecimal::add);BigDecimal avg = sum.divide(BigDecimal.valueOf(judges.size()), 2, BigDecimal.ROUND_HALF_UP);map.put("avgScore", avg);if (judges.size() > 3) {judges.remove(0);judges.remove(judges.size() - 1);BigDecimal sum1 = judges.stream().map(DyzScoreReviewWorksJudges::getScore).reduce(BigDecimal.ZERO, BigDecimal::add);BigDecimal avg1 = sum1.divide(BigDecimal.valueOf(judges.size()), 2, BigDecimal.ROUND_HALF_UP);map.put("avgScore1", avg1);}}for (int i = 0; i < 4; i++) {map.put("fileName" + (i + 1), "");}List<DyzScoreWorksFiles> files = fileMap.get(workId);if (files != null && files.size() > 0) {AtomicInteger serialNo = new AtomicInteger(0);files.forEach(f -> map.put("fileName" + serialNo.incrementAndGet(), f.getUrl()));}});ConcurrentHashSet<String> columns = views.stream().flatMap(map -> map.keySet().stream()).collect(Collectors.toCollection(ConcurrentHashSet::new));List<String> scoreColumns = columns.stream().filter(c -> c.contains("score") || c.contains("avgScore")).collect(Collectors.toList());List<String> correctnessColumns = columns.stream().filter(c -> c.contains("correctness")).collect(Collectors.toList());//输入流InputStream inputStream = null;ServletOutputStream outputStream = null;ExcelWriter excelWriter = null;try {org.springframework.core.io.Resource templateFile = resourceLoader.getResource("classpath:templates\\XXXX报表.xlsx");inputStream = templateFile.getInputStream();// 获取文件名并转码response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");outputStream = response.getOutputStream();// 创建填充配置FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();// 创建写对象excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream).build();// 创建Sheet对象WriteSheet sheet = EasyExcel.writerSheet(0, "报名数量统计").build();excelWriter.fill(views, fillConfig, sheet);excelWriter.fill(new FillWrapper("scoreColumns", scoreColumns), sheet);excelWriter.fill(new FillWrapper("correctnessColumns", correctnessColumns), sheet);} catch (Exception e) {log.error("导出失败={}", e.getMessage());} finally {if (excelWriter != null) {excelWriter.finish();}//关闭流if (outputStream != null) {try {outputStream.close();} catch (IOException e) {log.error("关闭输出流失败", e);}}if (inputStream != null) {try {inputStream.close();} catch (IOException e) {log.error("关闭输入流失败", e);}}}
}

模板里面的取值占位符写法

        还有一些表格宽度,高度自适应策略,美化风格的代码就不贴了,需要的话到我的资源中去下载。

相关文章:

Alibaba EasyExcel 导入导出全家桶

一、阿里巴巴EasyExcel的优势 首先说下EasyExcel相对 Apache poi的优势&#xff1a; EasyExcel也是阿里研发在poi基础上做了封装&#xff0c;改进产物。它替开发者做了注解列表解析&#xff0c;表格填充等一系列代码编写工作&#xff0c;并将此抽象成通用和可扩展的框架。相对p…...

使用Scala编写一个简单的决策树分类测试demo

使用Scala编写一个简单的决策树分类测试demo&#xff0c;我们可以基于Apache Spark的MLlib库来实现。这里将展示如何创建一个简单的决策树分类器&#xff0c;并用它来进行预测。我们将遵循以下步骤&#xff1a; 设置环境和导入必要的包。加载并准备数据集。构建决策树模型。使…...

SQLServer中使用ISNULL替换为指定的替换值

ISNULL (Transact-SQL) 适用于&#xff1a; SQL ServerAzure SQL 数据库Azure SQL 托管实例Azure Synapse Analytics 分析平台系统 (PDW)Microsoft Fabric 中的 SQL 分析端点Microsoft Fabric 中的仓库 NULL替换为指定的替换值。 1、语法 ISNULL ( check_expression , rep…...

69 mysql 中 is null 的实现

前言 Mysql 中我们偶尔会用到 字段为 NULL 的情况 这时候 我们只能使用查询 “select * from tz_test_02 where field1 is null;” 来进行 field1 字段为 null 的行的查询 然后如果是使用 “select * from tz_test_02 where field1 null;” 你会发现查询 不出数据 但是如…...

【信息系统项目管理师】第9章:项目范围管理-基础和过程 考点梳理

文章目录 9.1 管理基础9.1.1 产品范围和项目范围9.1.2 管理新实践 9.2 项目范围管理过程9.2.1 过程概述9.2.2 裁剪考虑因素9.2.3 敏捷与适应方法 【学习建议】本章节内容属于10大管理知识领域&#xff0c;选择、案例、论文都会考&#xff0c;建议重点学习。项目范围管理包括确保…...

智能制造标准体系建设指南

一、智能制造系统架构总览 智能制造作为当今制造业转型升级的核心&#xff0c;深度整合了新一代信息技术与传统制造工艺&#xff0c;催生出一个横跨产品全生命周期、纵贯多层级组织架构&#xff0c;并彰显多元智能特性的复杂系统。这一架构从生命周期、系统层级、智能特征三个…...

怎么获取键值对的键的数值?

问&#xff1a; 通过paelData.cardMap.C0002112可以获取到Cooo2112里面的数据&#xff0c;但是有时候接口返回的不是C0002112而是C0002093或者其他值&#xff0c;请问我该怎么写&#xff1f; 后端返回的数据是这样的&#xff1a; cardMap: { C0002112: { name: Item 1, va…...

反向代理后Request.Url.AbsoluteUri获取成了内网IP

出现的问题&#xff1a;用户请求的是域名&#xff0c;而后端通过Request.Url.AbsoluteUri获取用户请求的绝对路径时&#xff0c;变成了内网IP 解决方式&#xff1a; 反向代理配置中加上&#xff1a; proxy_set_header Host $host; # proxy_set_header Host h o s t : host: h…...

NLP论文速读(斯坦福大学)|使用Tree将语法隐藏到Transformer语言模型中正则化

论文速读|Sneaking Syntax into Transformer Language Models with Tree Regularization 论文信息&#xff1a; 简介&#xff1a; 本文的背景是基于人类语言理解的组合性特征&#xff0c;即语言处理本质上是层次化的&#xff1a;语法规则将词级别的意义组合成更大的成分的意义&…...

OpenCV-图像阈值

简单阈值法 此方法是直截了当的。如果像素值大于阈值&#xff0c;则会被赋为一个值&#xff08;可能为白色&#xff09;&#xff0c;否则会赋为另一个值&#xff08;可能为黑色&#xff09;。使用的函数是 cv.threshold。第一个参数是源图像&#xff0c;它应该是灰度图像。第二…...

window系统,照片应用打开图片,但是提示操作系统找不到已输入的环境选项,请问怎么解决,以便能打开图片

文章目录 问题描述问题解决一、先用AI提问&#xff0c;看能否得到解答二、最终的解决方法-修改环境变量 至此问题解决。 问题描述 Windows中&#xff0c;使用默认的图片应用打开图片时&#xff0c;提示&#xff1a; 操作系统找不到已输入的环境选项如下图&#xff1a; 这个问…...

Excel之查找函数-XLOOKUP

背景&#xff1a; 某些数据处理&#xff0c;需要对比两个乱序或者数据不完全相同的数值&#xff0c;取到另外一个数据值&#xff0c;数据量大的情况下&#xff0c;人工对比太耗时&#xff0c;XLOOKUP函数是一个查找函数&#xff0c;可以通过遍历对比某一单元格的值&#xff0c…...

深入理解 Java 内存管理:堆和栈

深入理解 Java 内存管理&#xff1a;堆和栈的全面解析 在 Java 编程语言中&#xff0c;内存管理是一个至关重要的概念&#xff0c;其中堆&#xff08;Heap&#xff09;和栈&#xff08;Stack&#xff09;是两个核心的内存区域。理解它们的工作原理、用途以及它们在程序执行过程…...

深度全解析开放开源大模型之BLOOM

BLOOM是 BigScience Large Open-science Open-access Mul-tilingual Language Model首字母的缩写。 BigScience 不是财团&#xff08;consortium&#xff09;&#xff0c;也不是正式成立的实体。这是一个由HuggingFace、GENCI和IDRIS发起的开放式协作组织&#xff0c;以及一个…...

下载谷歌浏览器的官方离线安装包

网址&#xff1a;https://support.google.com/chrome/answer/95346?hlzh-Hans&coGENIE.Platform%3DDesktop#zippy%2Cwindows...

ORACLE创建用户报错ORA-65096: invalid common user or role name

在高版本的oracle中创建用户时提示错误ORA-65096: invalid common user or role name&#xff0c;官网说明用户名必须使用C##或c##开头。以下方法亲测有效。 通过设置"_ORACLE_SCRIPT"参数为true来临时绕过CDB中创建用户必须以"C##"开头的限制。请注意&…...

河工oj第七周补题题解2024

A.GO LecturesⅠ—— Victory GO LecturesⅠ—— Victory - 问题 - 软件学院OJ 代码 统计 #include<bits/stdc.h> using namespace std;double b, w;int main() {for(int i 1; i < 19; i ) {for(int j 1; j < 19; j ) {char ch; cin >> ch;if(ch B) b …...

运维大屏与设备仪表盘:打造高效运维管理的视觉中枢

在快速发展的信息化时代&#xff0c;运维行业面临着前所未有的挑战。随着业务规模的不断扩大和系统复杂度的日益增加&#xff0c;如何高效、准确地监控和管理设备运行状态&#xff0c;成为运维团队亟待解决的问题。运维大屏与设备仪表盘作为运维管理的重要工具&#xff0c;为运…...

计算机视觉与医学的结合:推动医学领域研究的新机遇

目录 引言医学领域面临的发文难题计算机视觉与医学的结合&#xff1a;发展趋势计算机视觉结合医学的研究方向高区位参考文章结语 引言 计算机视觉&#xff08;Computer Vision, CV&#xff09;技术作为人工智能的重要分支&#xff0c;已经在多个领域取得了显著的应用成果&…...

使用setsockopt函数SO_BINDTODEVICE异常,Protocol not available

前言 最近在使用OLT的DHCP Server的时候发现一些异常现象&#xff0c;就是ONU发的一个vlan的discover包其他不同vlan的DHCP地址池也会收到&#xff0c;导致其他服务器也发了offer包&#xff0c;ONU同时会有多个ip地址。一开始是没有使用SO_BINDTODEVICE&#xff0c;后面查到使…...

rpm包转deb包或deb包转rpm包

Debian系&#xff08;Ubuntu、Deepin、麒麟Destop等&#xff09;用的安装包是deb的&#xff0c;Red Hat系&#xff08;CentOS、欧拉、麒麟Server等&#xff09;用的安装包是rpm的。 如果需要在Ubuntu上安装rpm&#xff0c;或需要在CentOS上安装deb&#xff0c;需要安装alien s…...

ChatGPT 和文心一言哪个更好用?

ChatGPT vs 文心一言&#xff1a;哪个更好用&#xff1f; 引言 在人工智能蓬勃发展的今天&#xff0c;聊天机器人已经成为我们生活和工作中不可或缺的一部分。你可能听说过ChatGPT和文心一言这两个热门的聊天机器人&#xff0c;它们分别来自OpenAI和百度。那么&#xff0c;究…...

T113-S3 Tina 存储类型修改

前面介绍了如何在 Tina 中添加新的板子&#xff0c;本节介绍如何修改板子存储类型。 1、确定存储类型 Tina 支持多种存储类型&#xff0c;包括 SD 卡、eMMC、SPI NAND、SPI NOR 等。在添加板子之前&#xff0c;需要确定板子使用的存储类型。 存储类型修改 在 device/config/…...

【css】基础(一)

本专栏内容为&#xff1a;前端专栏 记录学习前端&#xff0c;分为若干个子专栏&#xff0c;html js css vue等 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;css专栏 &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &a…...

Linux中inode

磁盘的空间管理 如何对磁盘空间进行管理&#xff1f; 假设在一块大小为500G的磁盘中&#xff0c;500*1024*1024524288000KB。在磁盘中&#xff0c;扇区是磁盘的基本单位&#xff08;一般大小为512byte&#xff09;&#xff0c;而文件系统访问磁盘的基本单位是4KB&#xff0c;因…...

verilog fpga 如果if语句==号后面是个表达式 运行不稳定

来自 文心一言 在Verilog中编写FPGA代码时&#xff0c;使用if语句进行条件判断是常见的操作。然而&#xff0c;当if语句中的条件判断使用的是表达式&#xff08;如后面是一个复杂的表达式&#xff09;&#xff0c;确实可能会遇到运行不稳定的问题。这通常是由于以下几个原因导…...

BFS广度优先搜索

广度优先搜索&#xff08;Breadth-First Search, BFS&#xff09;是一种用于遍历或搜索树或图的算法。 它从根节点开始&#xff0c;逐层访问每个节点&#xff0c;并在访问完一层后才访问下一层。BFS常用于寻找最短路径的问题。 下面将用实例一和实例二来实现BFS广度优先搜索 …...

uniapp 自定义导航栏增加首页按钮,仿微信小程序操作胶囊

实现效果如图 抽成组件navbar.vue&#xff0c;放入分包 <template><view class"header-nav-box":style"{height:Props.imgShow?:statusBarHeightpx,background:Props.imgShow?:Props.bgColor||#ffffff;}"><!-- 是否使用图片背景 false…...

小程序项目的基本组成结构

分类介绍 项目根目录下的文件及文件夹 pages文件夹 用来存放所有小程序的页面&#xff0c;其中每个页面都由4个基本文件组成&#xff0c;它们分别是&#xff1a; .js文件&#xff1a;页面的脚本文件&#xff0c;用于存放页面的数据、事件处理函数等 .json文件&#xff1a;…...

2020年国赛高教杯数学建模E题校园供水系统智能管理解题全过程文档及程序

2020年国赛高教杯数学建模 E题 校园供水系统智能管理 原题再现 校园供水系统是校园公用设施的重要组成部分&#xff0c;学校为了保障校园供水系统的正常运行需要投入大量的人力、物力和财力。随着科学技术的发展&#xff0c;校园内已经普遍使用了智能水表&#xff0c;从而可以…...

《C++与 OpenCV 机器学习模块:目标检测的强大融合》

在当今科技飞速发展的时代&#xff0c;目标检测技术已经成为计算机视觉领域的一颗璀璨明珠&#xff0c;广泛应用于安防监控、自动驾驶、智能图像分析等众多领域。而 C语言凭借其高效性和强大的性能&#xff0c;与 OpenCV 的机器学习模块相结合&#xff0c;为实现精准且高效的目…...

函数与模块

目录 1.函数 1.1.函数概念 1.2.函数定义 1.3.函数分类 1.4.函数参数类型 1.4.1.位置参数 1.4.2.关键字参数 1.4.3.默认值参数 1.4.4.可变参数 1.5.匿名函数 1.6.参数传递问题 2.冒泡排序 2.1.冒泡排序规则 2.2.冒泡排序实现 3.math 模块 4.random 模块 5.date…...

SQL面试题——滴滴SQL面试题 取出累计值与1000差值最小的记录

滴滴SQL面试题 取出累计值与1000差值最小的记录 今天的题目来自滴滴出行 已知有表cost_detail包含id和money两列,id为自增,请累加计算money值,并求出累加值与1000差值最小的记录。 +-----+--------+ | id | money | +-----+--------+ | 1 | 200 | | 2 | 300 …...

使用 Elastic 和 Amazon Bedrock 制作混合地理空间 RAG 应用程序

作者&#xff1a;来自 Elastic Udayasimha Theepireddy (Uday), Srinivas Pendyala, Ayan Ray 借助 Elasticsearch 及其向量数据库&#xff0c;你可以构建可配置的搜索和可信的生成式 AI (GenAI) 体验&#xff0c;这些体验可快速从原型扩展到生产。主要功能包括&#xff1a; 内…...

计算机软著个人申请指南

笔者最近申请了两本软著证书&#xff0c;结合自己的经历介绍一下。 软著登记网址&#xff1a;中国版权业务登记平台 准备工作 你的项目源码源代码统计工具 source-codes-trim&#xff1a;笔者开发自用的源码处理工具vscode插件其他的可以自己在网上搜资源 Word 申请流程 注…...

OmniParser一种用于增强视觉语言模型与用户界面交互效果的技术

OmniParser一种用于增强视觉语言模型与用户界面交互效果的技术 OmniParser的核心功能是将用户界面截图转换为结构化元素&#xff0c;这一过程涉及几个关键步骤和技术要素&#xff0c;解决了视觉语言模型&#xff08;VLMs&#xff09;在与用户界面交互时所面临的多种挑战。 1.…...

HBU深度学习作业10-BPTT

1、推导BPTT 2、推导梯度 3、分析其可能存在梯度爆炸的原因并给出解决方法 为了改善循环神经网络的长程依赖问题&#xff0c;一种非常好的解决方案是在公 式(6.50)的基础上引入门控机制来控制信息的累积速度&#xff0c;包括有选择地加入新 的信息&#xff0c;并有选择地遗忘之…...

L2G6000 - MindSearch部署实践

文章目录 MindSearch部署实践部署MindSearch到 hugging face Spaces上启动MindSearch启动后端启动前端 部署到自己的 HuggingFace Spaces上 部署MindSearch streamlit版本到Hugging Face上 MindSearch部署实践 部署MindSearch到 hugging face Spaces上 在Codespaces上选择Bla…...

CentOS 7 上安装 MySQL 8.0.40 (二进制安装)

要在 CentOS 7 上安装 MySQL 8.0.40&#xff0c;按照以下步骤操作&#xff1a; 下载安装包。 https://dev.mysql.com/downloads/mysql/ 下载之前查看系统c版本 解压安装包 首先&#xff0c;解压下载的 .tar.xz 安装包。 cd /path/to/your/downloads tar -xvf mysql-8.0…...

应用案例 | 船舶海洋: 水下无人航行器数字样机功能模型构建

水下无人航行器数字样机功能模型构建 一、项目背景 为响应水下装备系统研制数字化转型及装备系统数字样机建设的需要&#xff0c;以某型号水下无人航行器&#xff08;Underwater Unmanned Vehicle&#xff0c;UUV&#xff09;为例&#xff0c;构建UUV数字样机1.0功能模型。针对…...

Chromium CDP 开发(六):注册自己的指令(下)

引言 在这一章节中&#xff0c;我们将详细讲解如何将新定义的 TimerSend 指令和 TimerLog 事件添加到项目 的 inspector_protocol_config.json 文件中&#xff0c;从而使这些功能能够在 CDP&#xff08;Chrome DevTools Protocol&#xff09;中被识别并正常使用。 inspector_pr…...

【基础算法总结】哈希表/set/map篇

目录 一&#xff0c;哈希表简介二&#xff0c;算法原理和代码实现1.两数之和349.两个数组的交集面试题01.02.判断是否互为字符重排217.存在重复元素219.存在重复元素II692.前k个高频单词45.字母异位词分组 三&#xff0c;算法总结 一&#xff0c;哈希表简介 哈希思想是算法中一…...

数字逻辑理论题目+知识点复习

昨天考完的数字逻辑理论考试&#xff0c;啊啊啊还有实验&#xff0c;生活不易&#xff0c;且行且珍惜。 请以最大的善意对待生活(,,>᎑<,,)。...

证明网络中的流形成一个凸集

证明网络中的流形成一个凸集 步骤1&#xff1a;定义和符号步骤2&#xff1a;线性组合步骤3&#xff1a;验证容量限制步骤4&#xff1a;验证流量守恒结论示例代码&#xff08;C语言&#xff09; 在网络流理论中&#xff0c;一个流 f f f 是定义在网络图的边集上的一种函数&…...

SpringBoot3

1. 配置文件 1. 基本使用 使用 配置文件classpath:application.properties spring.jdbc.drivercom.mysql.cj.jdbc.Driver spring.jdbc.urljdbc:mysql://localhost:3306/batis spring.jdbc.usernameroot spring.jdbc.password123456使用配置文件的值&#xff1a;Value("…...

Linux之线程概念,理解和控制

Linux之线程概念&#xff0c;理解和控制 一.线程1.1线程的概念1.2线程的理解1.3线程的优缺点&#xff0c;异常和用途1.4线程和进程1.5线程的控制1.4.1线程的创建1.4.2线程的终止1.4.3线程的等待1.4.4线程的分离 一.线程 1.1线程的概念 在我们了解了进程的同时我们在学校上课时…...

系统监控——分布式链路追踪系统

摘要 本文深入探讨了分布式链路追踪系统的必要性与实施细节。随着软件架构的复杂化&#xff0c;传统的日志分析方法已不足以应对问题定位的需求。文章首先解释了链路追踪的基本概念&#xff0c;如Trace和Span&#xff0c;并讨论了其基本原理。接着&#xff0c;文章介绍了SkyWa…...

【Leetcode Top 100】146. LRU 缓存

问题背景 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 c a p a c i t y capacity capacity 初始化 LRU 缓存int get(int key) 如果关键字 k e y key key 存在于缓存中&…...

Ubuntu Server 22.04.5 LTS重启后IP被重置问题

Ubuntu Server 22.04.5 LTS重启后IP被重置问题 最近在使用Ubuntu Server 22.04做项目开发测试时发现每次重启和关机后&#xff0c;所设置的静态IP地址都会回复到安装系统时所设置的ip Ubuntu Server 22.04 官网下载地址&#xff1a;Ubuntu官方下载地址 对虚拟机下安装Ubuntu感…...

电机功率、电压与电流的换算方法

在电气工程和相关行业中&#xff0c;电机的功率、电压和电流是三个重要的基本参数。它们之间有着密切的关系&#xff0c;而理解这些关系对于电机的选型、设计和应用至关重要。本文将详细阐述这三者之间的换算关系&#xff0c;以及相关公式的应用。 一、电机功率的定义 电机功…...