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

java动态导入导出excel,javassist动态创建类

Java动态解析excel

1、使用的技术

  • javassist

Javassist 使 Java 字节码操作变得简单。它是一个用于在 Java 中编辑字节码的类库。它使 Java 程序可以在运行时定义新类,并在 JVM 加载它时修改类文件。与其他类似的字节码编辑器不同,Javassist 提供了两个级别的 API:源级别和字节代码级别。如果用户使用源代码级 API,则他们可以在不了解 Java 字节码规范的情况下编辑类文件。整个 API 仅使用 Java 语言的词汇表进行设计。您甚至可以以源文本的形式指定插入的字节码。Javassist 可以即时对其进行编译。另一方面,字节码级别的 API 允许用户像其他编辑器一样直接编辑类文件。

  • easyPOI

Easypoi 是一个基于 Apache POI 的 Java 库,极大地简化了 Excel 文件的读写操作。它提供了一套简单易用的 API,使得开发者可以高效地处理 Excel 数据导入导出任务,无需深入学习复杂的 POI 库细节。Easypoi 支持模板导出、数据验证、自定义样式等多种功能,广泛应用于报表生成、数据预处理等场景。

快速入门可以看我的这篇文章:JAVA操作Excel(POI、easyPOI、easyExcel)

2、包导入(基于SpringBoot)

        <!--easypoi--><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.4.0</version></dependency><!-- javassist easypoi 自带了,可以不用引入 下图有展示 -->
<!--        <dependency>-->
<!--            <groupId>org.javassist</groupId>-->
<!--            <artifactId>javassist</artifactId>-->
<!--            <version>3.25.0-GA</version>-->
<!--        </dependency>--><!-- easypoi 的 javassist 如果和其他包的冲突了,可以使用以下代码排除掉该包的 javassist 下图有展示 -->
<!--        <dependency>-->
<!--            <groupId>cn.afterturn</groupId>-->
<!--            <artifactId>easypoi-spring-boot-starter</artifactId>-->
<!--            <version>4.4.0</version>-->
<!--            <exclusions>-->
<!--                <exclusion>-->
<!--                    <groupId>org.javassist</groupId>-->
<!--                    <artifactId>javassist</artifactId>-->
<!--                </exclusion>-->
<!--            </exclusions>-->
<!--        </dependency>-->

依赖有冲突示意图

在这里插入图片描述

3、代码(注意:我下面有时说的字段、有时说的属性,是一个东西)

(1)、动态创建类的工具类


/*** 动态生成 含有注解 @Excel 的实体工具类*/
public class CreateClassUtils {// class 所在的堆名public static final String CLASS_NAME_PREFIX = "com.example.test.pojo.CreateBeanDto@";// easyPOI 注解的类包名public static final String ANNOTATION_PACKAGE_NAME = "cn.afterturn.easypoi.excel.annotation.Excel";// 目前只能使用 字符串public static final String STRING_PACKAGE_NAME = "java.lang.String";/*** 动态生成类*/public static Class<?> generatePrototypeClass(List<BeanDto> list) throws NotFoundException, CannotCompileException, IOException {// 类目拼接String className = CLASS_NAME_PREFIX + UUID.randomUUID().toString();// 类池ClassPool pool = ClassPool.getDefault();// 往池子里面新增一个 类 赋名字CtClass clazz = pool.makeClass(className);// 获取类文件ClassFile ccFile = clazz.getClassFile();// 获取文件常量池ConstPool constpool = ccFile.getConstPool();// 添加字段addExpressField(pool, clazz, constpool, list);return clazz.toClass();}private static void addExpressField(ClassPool pool, CtClass clazz, ConstPool constpool, List<BeanDto> list) throws CannotCompileException, NotFoundException {// 将数据库查出动态附上 属性for (BeanDto bean : list) {// 属性需要小写 toLowerCase()addFieldAndAnnotation(pool, clazz, constpool, bean.getAnnotationName(), bean.getProperty().toLowerCase());}}/*** 添加字段和注解* 目前只支持字符串 其实基本上也够了 可以拿到字符串转成其他的类型* 如果后续我又兴趣会更新其他类型的*/private static void addFieldAndAnnotation(ClassPool pool, CtClass clazz, ConstPool constpool, String titleName, String fieldName) throws NotFoundException, CannotCompileException {// 生成fieldCtField field = new CtField(pool.get(STRING_PACKAGE_NAME), fieldName, clazz);field.setModifiers(Modifier.PRIVATE);// 添加 easyPoi 的注解AnnotationsAttribute fieldAttr = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);Annotation annotation = new Annotation(ANNOTATION_PACKAGE_NAME, constpool);// 添加注解名 name 的含义就是指 表头annotation.addMemberValue("name", new StringMemberValue(titleName, constpool));// 添加宽度annotation.addMemberValue("width", new DoubleMemberValue(30.0, constpool));fieldAttr.addAnnotation(annotation);field.getFieldInfo().addAttribute(fieldAttr);// 生成get/set方法clazz.addMethod(CtNewMethod.getter("get" + upperFirstLatter(fieldName), field));clazz.addMethod(CtNewMethod.setter("set" + upperFirstLatter(fieldName), field));// 类 添加属性clazz.addField(field);}/*** 拼接 get/set 方法*/public static String upperFirstLatter(String letter) {return letter.substring(0, 1).toUpperCase() + letter.substring(1);}/*** 需要动态生成类的参数*/@Datapublic static class BeanDto {/*** 属性名*/private String property;/*** 注解名*/private String annotationName;}
}

(2)、测试类


@RestController
@Slf4j
@RequestMapping("/test")
public class TestController {/*** 导出实体*/@Datapublic static class ImportExcelDto {/*** 注解名*/private String[] annotationName;/*** 属性*/private String[] property;/*** 文件*/private MultipartFile file;}/*** 导入实体*/@Datapublic static class ExportExcelDto {/*** 注解名*/private String[] annotationName;/*** 属性*/private String[] property;}/*** 导出* 参数示意:* {*     "property":["code","name","phone"],*     "annotationName":["编码","名字","电话"]* }*/@PostMapping("/exportExcel")public void exportExcel(@RequestBody ExportExcelDto dto, HttpServletResponse response) throws Exception {try {exportExcelService(dto, response);} catch (Exception e) {log.error("exportExcel 报错", e);}}/*** 导入* 参数示意:// 表头字段String[] annotationName = new String[]{"名字", "电话"};// 属性字段String[] property = new String[]{"name", "phone"};*/@PostMapping("/importExcel")public Results importExcel(ImportExcelDto dto) throws Exception {try {importExcelService(dto);return Results.success();} catch (Exception e) {log.error("importExcel 报错", e);return Results.failed(e.getMessage());}}public void exportExcelService(ExportExcelDto dto, HttpServletResponse response) throws Exception {String[] name = dto.getAnnotationName();String[] property = dto.getProperty();// 模拟数据String datas = "[\n" +"    {\n" +"        \"code\":\"111\",\n" +"        \"name\":\"222\",\n" +"        \"phone\":\"333\"\n" +"    },\n" +"    {\n" +"        \"code\":\"444\",\n" +"        \"name\":\"555\",\n" +"        \"phone\":\"666\"\n" +"    },\n" +"    {\n" +"        \"code\":\"777\",\n" +"        \"name\":\"888\",\n" +"        \"phone\":\"999\"\n" +"    }\n" +"]";if (name.length != property.length) {throw new Exception("入参错误");}// 遍历成我们需要的 值List<CreateClassUtils.BeanDto> beanDtos = new ArrayList<>();for (int i = 0; i < name.length; i++) {CreateClassUtils.BeanDto beanDto = new CreateClassUtils.BeanDto();beanDto.setAnnotationName(name[i]);beanDto.setProperty(property[i]);beanDtos.add(beanDto);}JSONArray data = JSONArray.parseArray(datas);// 动态生成excelClass<?> salaryArchivesClass = CreateClassUtils.generatePrototypeClass(beanDtos);//列名List<ExcelExportEntity> headList = new ArrayList<>();// 遍历表头for (CreateClassUtils.BeanDto beanDto : beanDtos) {String annotationName = beanDto.getAnnotationName();String key = beanDto.getProperty();// 这个 annotationName 就是excel的表头 key与导出的属性值对应// 也可以使用 salaryArchivesClass 中 注解 Excel 的 name 值ExcelExportEntity exportEntity = new ExcelExportEntity(annotationName, key);// 反射获取属性(字段)Field field = salaryArchivesClass.getDeclaredField(key);// 获取动态生成的 Excel 注解的 width 值if (field.isAnnotationPresent(Excel.class)) {Excel annotation = field.getAnnotation(Excel.class);double width = annotation.width();exportEntity.setWidth(width);}headList.add(exportEntity);}// 导出EasyPoiUtil.exportExcel(null, "666", "666.xlsx", headList, data, response);}/*** 导入服务*/public void importExcelService(ImportExcelDto dto) throws Exception {String[] annotationName = dto.getAnnotationName();String[] property = dto.getProperty();if (annotationName.length != property.length) {throw new Exception("入参错误");}List<CreateClassUtils.BeanDto> beanDtos = new ArrayList<>();// 把参数往 塞入 BeanDtofor (int i = 0; i < annotationName.length; i++) {CreateClassUtils.BeanDto beanDto = new CreateClassUtils.BeanDto();beanDto.setAnnotationName(annotationName[i]);beanDto.setProperty(property[i]);beanDtos.add(beanDto);}// 创建类Class<?> salaryArchivesClass = CreateClassUtils.generatePrototypeClass(beanDtos);// 新建实例Object o = salaryArchivesClass.newInstance();// 获取 CreateBeanDto 中的 get 方法Method get = salaryArchivesClass.getDeclaredMethod("get" + CreateClassUtils.upperFirstLatter(property[0].toLowerCase()));Object invoke = get.invoke(o);log.info("Object = {}", o);log.info("get = {}", invoke);List<?> objects = EasyPoiUtil.importExcel(dto.getFile(), 0, 1, salaryArchivesClass);String jsonString = JSON.toJSONString(objects);log.info("jsonString = {}", jsonString);}
}

(3)、easyPOI工具类

/*** @author ZHAOPINGAN* @Title: EasyPoiUtil* @ProjectName* @Description:*/
@Slf4j
public class EasyPoiUtil {/*** Excel大数据导入** @param file       文件信息* @param titleRows 标题占几行,从哪行开始读取 ,默认 0* @param headerRows header占几行 ,默认 1* @param pojoClass     对象Class*/public static <T> List<T> importBigExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws Exception {if (file == null) {throw new Exception("未找到上传的文件!");}ImportParams params = new ImportParams();params.setTitleRows(titleRows);params.setHeadRows(headerRows);params.setNeedVerify(true); // 开启校验规则List<T> list = new ArrayList<>();try {log.info("正在读取文件: " + file.getOriginalFilename() + ",开始导入数据。");ExcelImportUtil.importExcelBySax(file.getInputStream(), pojoClass, params, new IReadHandler<T>() {@Overridepublic void handler(T o) {list.add(o);}@Overridepublic void doAfterAll() {}});} catch (Exception e) {log.error("导入错误",e);} finally {file.getInputStream().close();}return list;}/*** Excel 普通根据本地路径导入* @param filePath 本地文件路径* @param titleRows 标题占几行,从哪行开始读取 ,默认 0* @param headerRows header占几行 ,默认 1* @param pojoClass 对象Class*/public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) {if (StringUtils.isBlank(filePath)) {return null;}ImportParams params = new ImportParams();params.setTitleRows(titleRows);params.setHeadRows(headerRows);List<T> list = null;try {list = ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);} catch (NoSuchElementException e) {throw new ExcelImportException("模板不能为空");} catch (Exception e) {e.printStackTrace();throw new ExcelImportException(e.getMessage());}return list;}/*** Excel 普通导入* @param file  文件信息* @param titleRows 标题占几行,从哪行开始读取 ,默认 0* @param headerRows header占几行 ,默认 1* @param pojoClass 对象Class*/public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) {if (file == null) {return null;}ImportParams params = new ImportParams();params.setTitleRows(titleRows);//标题占几行,从哪行开始读取params.setHeadRows(headerRows);//header占几行params.setSheetNum(1);List<T> list = null;try {list = ExcelImportUtil.importExcel(file.getInputStream(), pojoClass, params);} catch (NoSuchElementException e) {throw new ExcelImportException("excel文件不能为空");} catch (Exception e) {throw new ExcelImportException(e.getMessage());}return list;}/*** 导出excel** @param list           数据list* @param title          标题* @param sheetName      sheet名称* @param pojoClass      实体的class* @param fileName       文件名称* @param isCreateHeader 是否创建头* @param response       响应*/public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, boolean isCreateHeader, HttpServletResponse response) {ExportParams exportParams = new ExportParams(title, sheetName);exportParams.setCreateHeadRows(isCreateHeader);defaultExport(list, pojoClass, fileName, response, exportParams);}/*** 导出excel** @param list      数据list* @param title     标题* @param sheetName sheet名称* @param pojoClass 实体class* @param fileName  文件名* @param response  响应*/public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, HttpServletResponse response) {defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName));}/*** 导出excel2*/public static void exportExcel(String title, String sheetName, String fileName, List<ExcelExportEntity> entityList, List<?> list, HttpServletResponse response) {defaultExport(list, entityList, fileName, response, new ExportParams(title, sheetName));}/*** 导出excel** @param list     多个Map key title 对应表格Title key entity 对应表格对应实体 key data* @param fileName 标题* @param response 响应*/public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response) {defaultExport(list, fileName, response);}/*** 返回字节码** @param list      数据list* @param title     标题* @param sheetName sheet名称* @param pojoClass 实体class*/public static ByteArrayOutputStream exportExcelBackByte(List<?> list, String title, String sheetName, Class<?> pojoClass) throws IOException {Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(title, sheetName), pojoClass, list);ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();workbook.write(byteArrayOutputStream);return byteArrayOutputStream;}/*** Excel大数据导出** @param list          数据List* @param pojoClass     对象Class*/public static void exportBigExcelByte(Collection<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response) throws Exception {Workbook workbook;// 设置导出单sheet页最大一百万行数据ExportParams exportParams = new ExportParams();exportParams.setMaxNum(1000000);exportParams.setType(ExcelType.XSSF);workbook = ExcelExportUtil.exportBigExcel(exportParams, pojoClass, (queryParams, num) -> {// 只导出一次,第二次返回null终止循环if (((int) queryParams) == num) {return null;}log.info("正在进行大数据量导出,条数: " + list.size());return Arrays.asList(list.toArray());}, 2);OutputStream outputStream = null;try {response.setCharacterEncoding("UTF-8");response.setContentType("application/vnd.ms-excel");response.setHeader(HttpHeaders.CONTENT_DISPOSITION,"attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")+ ";" + "filename*=utf-8''" + URLEncoder.encode(fileName, "UTF-8"));outputStream = response.getOutputStream();workbook.write(outputStream);//保存Excel文件if (outputStream != null) {outputStream.close();//关闭文件流}} catch (Exception e) {log.info("execel流输出时错误,错误详情:{}", e.getMessage());throw new Exception("execel流输出时错误");}}/*** 导出多个excel** @param workbooks 多个excel文件 通过ExcelExportUtil.exportExcel往workbooks内放入excel* @param fileNames 文件名 每个excel文件的名称顺序必须一致且名称请务必保证不重复* @param fileName  压缩包文件名* @param response  标题*/public static void exportExcels(List<Workbook> workbooks, List<String> fileNames, String fileName, HttpServletResponse response) {try {
//            response.setHeader("Content-Disposition",
//                "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".zip");response.setHeader(HttpHeaders.CONTENT_DISPOSITION,"attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")+ ";"+ "filename*=utf-8''" + URLEncoder.encode(fileName, "UTF-8") + ".zip");OutputStream toClient = new BufferedOutputStream(response.getOutputStream());ZipOutputStream zipOut = new ZipOutputStream(toClient);for (int i = 0; i < workbooks.size(); i++) {ZipEntry entry = new ZipEntry(fileNames.get(i) + ".xls");zipOut.putNextEntry(entry);workbooks.get(i).write(zipOut);}zipOut.flush();zipOut.close();} catch (IOException e) {throw new ExcelExportException(e.getMessage());}}/*** 导出excel** @param list      数据list* @param pojoClass 实体class* @param fileName  文件名* @param response  响应*/private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) {Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);downLoadExcel(fileName, response, workbook);}/*** 导出excel2*/private static void defaultExport(List<?> list, List<ExcelExportEntity> entityList, String fileName, HttpServletResponse response, ExportParams exportParams) {Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, list);downLoadExcel(fileName, response, workbook);}private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) {try {response.setCharacterEncoding("UTF-8");response.setHeader(HttpHeaders.CONTENT_DISPOSITION,"attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")+ ";" + "filename*=utf-8''" + URLEncoder.encode(fileName, "UTF-8"));response.setContentType("application/vnd.ms-excel;charset=UTF-8");response.setHeader(HttpHeaders.PRAGMA, "no-cache");//关闭缓存response.setHeader(HttpHeaders.CACHE_CONTROL, "no-cache");//关闭缓存workbook.write(response.getOutputStream());workbook.close();} catch (IOException e) {throw new ExcelImportException(e.getMessage());}}private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) {Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF);downLoadExcel(fileName, response, workbook);}
}

(4)、json返回类(可以改成lombok的,我懒得改了)

/*** 用于返回* @param <T>*/
@ApiModel("统一返回类")
public class Results<T> {public static final String ERROR = "500";public static final String SUCCESS = "200";/*** 返回码*/@ApiModelProperty("返回码,正确码为:200")private String resCode ;/*** 返回消息*/@ApiModelProperty("返回消息")private String msg ;/*** 返回实体*/@ApiModelProperty("返回实体")private T obj;public static <T> Results<T> success(){return success(SUCCESS,"成功",null);}public static <T> Results<T> success(String msg){return success(SUCCESS,msg,null);}public static <T> Results<T> success(T obj){return success(SUCCESS,"成功",obj);}public static <T> Results<T> success(String msg,T obj){return success(SUCCESS,msg,obj);}public static <T> Results<T> success(String resCode,String msg,T obj){Results<T> result = new Results<T>();result.setResCode(resCode);result.setMsg(msg);result.setObj(obj);return result;}public static <T> Results<T> failed() {return failed(ERROR,"失败",null);}public static <T> Results<T> failed(String msg) {return failed(ERROR,msg,null);}public static <T> Results<T> failed(String msg,T obj) {return failed(ERROR,msg,obj);}public static <T> Results<T> failed(String resCode,String msg) {return failed(resCode,msg,null);}public static <T> Results<T> failed(Integer resCode,String msg) {return failed(String.valueOf(resCode),msg);}public static <T> Results<T> failed(String resCode,String msg,T obj) {Results<T> result = new Results<T>();result.setResCode(resCode);result.setMsg(msg);result.setObj(obj);return result;}public String getResCode() {return resCode;}public void setResCode(String resCode) {this.resCode = resCode;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public T getObj() {return obj;}public void setObj(T obj) {this.obj = obj;}@Overridepublic String toString() {return "Results{" +"resCode='" + resCode + '\'' +", msg='" + msg + '\'' +", obj=" + obj +'}';}
}

4、测试

(1)、导出

在这里插入图片描述

在这里插入图片描述

(2)、导入(使用导出的666.xlsx,就只用名字和电话两个字段来展示)

在这里插入图片描述
在这里插入图片描述

我的其他关于excel的文章(里面的工具类都有些许不同,我根据实际情况都有所删减和添加)

JAVA操作Excel(POI、easyPOI、easyExcel)
https://blog.csdn.net/qq_57581439/article/details/131206886


http://www.mrgr.cn/news/65288.html

相关文章:

  • 二百七十六、ClickHouse——Hive和ClickHouse非常不同的DWS指标数据SQL语句
  • 动态规划—目标和
  • 提升安全上网体验:Windows 11 启用 DOH(阿里公共DNS)
  • 力扣题目解析--罗马数字转整型
  • Java实战项目-基于微信小程序的校园生活互助服务小程序
  • 图传推流学习(敬请期待)
  • C/C++ stackful 有栈协同程式的一些缺点。
  • django电商易购系统-计算机设计毕业源码61059
  • JAVA通过AOP自定义注解记录日志
  • 100种算法【Python版】第38篇—— Tarjan算法
  • 智能推荐系统介绍
  • 【人工智能-初级】练习题:matplotlib基础练习30例
  • Python 中的迭代器与生成器详解
  • 关于halcon的可变形logo模板匹配find_local_deformable_modle_xld解释及简化匹配代码
  • JavaScript函数
  • 物联网赋能的人工智能图像检测系统
  • 探索 Python 的新天地:Helium 库揭秘
  • 代码随想录训练营Day15 | 530.二叉搜索树的最小绝对差 - 501.二叉搜索树中的众数 - 236. 二叉树的最近公共祖先
  • 15.函数的重载
  • 04741计算机网络原理真题-CRC的计算-案例分析
  • PHP+MySQL开发的一套招聘管理系统开发案例源码功能介绍
  • H5页面在线预览pdf
  • 照明灯十大知名品牌有哪些?2024灯具十大公认品牌排行榜出炉!
  • SpringMVC课时2
  • FFmpeg 4.3 音视频-多路H265监控录放C++开发十. 多线程控制帧率。循环播放,QT connect 细节,
  • SpringBoot新闻稿件管理系统:开发与实践