머리말
최근 이지포이에 대해 한 독자님이 질문을 주셔서 시간을 내어 글을 정리하게 되었습니다.
Text
EasyPOI의 기능은 Easy라는 이름과 같습니다. 주요 기능은 Easy입니다. POI를 접해본 적이 없는 사람도 쉽게 Excel 내보내기, Excel 템플릿 내보내기, Excel 가져오기, Word를 작성할 수 있습니다. 템플릿 내보내기. 간단한 주석과 템플릿 언어(익숙한 표현 구문)를 통해 기존의 복잡한 작성 방법을 완성할 수 있습니다.
이 기사에서는 주로 간단한 분석을 사용하여 독자들에게 Excel 템플릿 작성 방법과 EasyPOI를 사용하여 필요에 맞는 Excel 데이터를 내보내는 방법을 알려 코딩을 단순화합니다. 동시에 이 기사에서는 독자가 함정을 피할 수 있도록 이미지 내보내기 기능과 같은 몇 가지 일반적이지 않은 기능도 설명합니다.
EasyPOI4.0.0 이상 버전은 Apache POI 4.0.0 이상 버전에 종속됩니다. 따라서 Maven 구성에서는 두 버전의 버전 번호가 일치해야 합니다.
아파치 POI 4.0.0은 이전 버전과 비교하여 상당한 변화가 있다는 점에 유의해야 합니다. 이전 코드의 Excel 작업 부분이 이전 버전에 따라 달라지는 경우 4.0.0 이상 버전을 사용하지 않는 것이 좋습니다. 물론, 이전 코드가 WorkBook 생성의 세부 사항을 포함하지 않거나 거의 포함하지 않는다면 새 버전을 사용하는 데 문제가 없습니다.
작성자가 다시 작성해야 하는 프로젝트는 JEECG 버전 3.7을 기반으로 하며 Apache POI 버전 3.9를 사용합니다. JEECG에서 유지 관리하는 최고 jeasypoi 버전은 2.2.0에 불과하며 이 버전은 템플릿 내보내기 이미지 기능을 지원하지 않습니다. 그러고 보니 다음 JEECG 팀에 대해 불만을 토로하고 싶습니다. jeasypoi를 유지할 계획이 없으니 그냥 공식 EasyPOI를 프로젝트에 직접 사용하면 되지 않겠습니까? ?
이전 버전과 호환되고 EasyPOI에서 가져온 이미지 내보내기 기능을 사용하기 위해 최종 채택한 EasyPOI 버전은 3.3.0이고 해당 Apache POI 종속성은 3.15입니다.
Maven 구성은 다음과 같습니다.
<properties> <poi.version>3.15</poi.version> <easypoi.version>3.3.0</easypoi.version> </properties> <dependencies> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>${poi.version}</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>${poi.version}</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>${poi.version}</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>${poi.version}</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-web</artifactId> <version>${easypoi.version}</version> </dependency> </dependencies>
저희는 엑셀 보고서의 스타일을 코딩으로 디자인하고 싶지 않기 때문에 EasyPOI의 템플릿 내보내기 기능을 사용합니다. 작업 단계는 Excel 템플릿을 디자인하고 고정된 부분과 루프에 채워야 하는 부분을 명확하게 구분하는 것입니다. EasyPOI에는 고유한 표현 언어가 있습니다. 각 표현에 대한 자세한 소개는 아래 참조 링크를 참조하세요.
일부 단순 템플릿은 여기서 자세히 설명하지 않고 렌더링 및 템플릿 구성 내용만 표시합니다. 독자가 복잡한 템플릿을 만들고 값을 입력하는 방법을 이해하면 간단한 템플릿도 빠르게 이해할 수 있습니다.
먼저 보고서 렌더링을 살펴보세요.
그런 다음 실제 템플릿을 살펴보세요.
위 두 사진을 보고 벌써 템플릿 내보내기 기능의 위력을 느끼셨나요?
아래에서 소개할 템플릿은 한 행이 하나의 레코드인 일반적인 상황과 달리 상대적으로 복잡하기 때문에 템플릿의 구성을 자세히 소개하고, the way EasyPOI의 몇 가지 표현에 대해 간략하게 소개합니다.
먼저 렌더링을 살펴보겠습니다.
그런 다음 템플릿을 살펴보세요.
이 두 사진을 비교해 보면 지식이 운명을 바꿀 수 있다고 생각하시나요?
템플릿 사진과 상품정보 렌더링을 보면 실제로 전체 템플릿이 상단과 하단으로 나누어져 있는 것을 확인할 수 있었습니다. 상단은 변경되지 않은 헤더정보이고, 하단은 주기적으로 삽입되는 상품상세정보입니다. 그래서 후반부에는 문법에 중점을 둡니다.
사진 하단의 첫 번째 열은 완전히 보이지 않고 실제로는 {{!fe: list t.id.
참고로 여기에는 }} 기호가 없습니다! EasyPOI의 공식 문서에 따르면 {{}}는 표현식을 나타내며 내부 값은 표현식에 따라 얻어집니다. 그림을 자세히 보면 그림의 오른쪽 하단에 표현식의 닫는 기호 {{}}가 나타나는 것을 볼 수 있습니다. 즉, 첫 번째 열 {{ 오른쪽 아래 모서리에서 끝납니다}}부터 시작하여 그 사이의 모든 내용이 표현식의 일부입니다.
전체 템플릿 정보가 표현식의 일부이기 때문에 일반 문자열이라도 특별히 표시해야 합니다. 표현식의 하위 표현식은 아래에서 하나씩 설명됩니다.
!fe: 행을 생성하지 않고 데이터를 탐색합니다.
공식 문서에 있는 이 문장은 모든 사람이 이해하기에는 다소 혼란스러울 수 있습니다. 행을 만들지 않는다는 것은 무엇을 의미합니까? 실제로 행을 생성하지 않는 것은 행을 생성하는 것과 관련이 있으며 행을 생성하는 표현식은 fe:입니다.
데이터베이스의 각 레코드가 엔터티 개체에 해당하는 것처럼 행을 생성한다는 것은 각 행이 엔터티 개체임을 의미합니다. 이 엔터티 개체의 속성은 {{}} 표현식으로 래핑됩니다.
행을 생성하지 않는다는 것은 전체 표현식에 하나의 개체 개체만 있다는 것을 의미하지만 이 개체는 목록에서 N개 개체로 이어져 있다는 의미입니다. 각 Entity는 모델 자체를 참조할 뿐만 아니라, 차지하는 셀 수, 셀의 좌표, 배열 순서 등 Excel 스타일도 포함합니다.
list는 표현식의 데이터 컬렉션을 나타내는 사용자 정의 이름입니다. 코드는 List를 키로 사용하여 Map
이름 목록은 이해하기 쉬운 자리 표시자이므로 부담 없이 선택할 수 있습니다. EasyPOI는 목록을 구문 분석할 때 Map
Java Zhiyin 공식 계정을 검색하여 "Backend Interview"라고 답장하시면 컬렉션의 모든 개체를 나타내는 사전 정의된 값인 Java 면접 질문 가이드.pdf
t를 제공해 드립니다.
리스트의 각 객체가 t라고 불리는 것을 템플릿에서 대략적으로 느낄 수 있으며, t.name은 t의 이름 속성을 나타내므로 이름 t는 어쨌든 목록과 동일합니다. 자리표시자 역할을 합니다.
하지만 사실 이곳은 큰 구덩이입니다! t를 g와 같은 다른 값으로 바꾸고 템플릿의 다른 곳에 g.name g.code 등을 쓰면 궁극적으로 구문 분석되지 않습니다! 공식 문서에서는 이 점을 강조하지 않지만, 저자는 실제로 함정을 밟고 나서야 이 점을 발견했습니다!
]] 개행 문자 여러 줄 순회 내보내기.
이 기호에 대한 공식적인 설명도 당황스럽습니다. 여러 줄 순회 및 내보내기가 무엇인가요? 실제로 이것이 의미하는 바는 표현식에 이 기호가 포함되어 있으면 그 뒤에 다른 내용이나 스타일이 있는지 여부에 관계없이 줄 뒤의 내용이 구문 분석되지 않는다는 것입니다.
이 기호는 각 행의 마지막 열에 작성되어야 합니다. 그렇지 않으면 행과 열의 수가 달라지며 EasyPOI가 내부 할당을 수행할 때 널 포인터 예외가 보고됩니다.
'' 작은따옴표는 상수 값을 나타냅니다. '' 예를 들어 '1'이면 출력은 1입니다.
공식 문서의 여기 소개에도 함정이 있습니다. ''는 상수 값을 의미하지만 실제로 Excel에서만 이것을 갖는 것은 잘못된 것입니다. 왜냐하면 Excel이 셀에서 '를 발견하면 다음이 모두 문자열이라고 생각하기 때문에 '' 라이브러리 유형: '을 작성해야 하기 때문입니다. 셀. , 표시되는 내용은 문자열 라이브러리 유형:'이 아니라 '라이브러리 유형:'입니다.
위 분석 결과, 사진 속 템플릿은 실제로 다음과 유사합니다.
{{!fe: list t.id ‘库别:’ t.bin 换行 ‘商品名称:’ t.name 换行 ‘商品编号:’ t.code t.barcode 换行 ‘生产日期:’ t.proDate 换行 ‘进货日期:’ t.recvDate}}
如果list中有多条记录,上述字符串就再循环拼接一些内容,最终都在一个{{}}表达式中。
至此,模板的设计已剖析完毕,读者可根据自己的需求结合官方文档自行设计模板。下面将对模板赋值进行介绍。
从上节的描述中可知,只需要准备一个Map994a833a6ffa28d85b72cb15422c29d6的对象即可,其中键为list,值为一个List数组,数组中元素类型为Map994a833a6ffa28d85b72cb15422c29d6。代码如下:
Map<String, Object> total = new HashMap<>(); List<Map<String, Object>> mapList = new ArrayList<>(); for (int i = 1; i <= 5; i++) { Map<String, Object> map = new HashMap<>(); map.put("id", i + ""); map.put("bin", "001 1000千克"); map.put("name", "商品" + i); map.put("code", "goods" + i); map.put("proDate", "2019-05-30"); map.put("recvDate", "2019-07-07"); // 插入图片 ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); BufferedImage bufferImg = ImageIO.read(BarcodeUtil.generateToStream("001")); ImageIO.write(bufferImg, "jpg", byteArrayOut); ImageEntity imageEntity = new ImageEntity(byteArrayOut.toByteArray(), 200, 1000); map.put("barcode", imageEntity); mapList.add(map); } total.put("list", mapList);
上述代码中需要特殊关注的是图片导出部分。EasyPOI导出图片有两种方式,一种是通过图片的Url,还有一种是获取图片的byte[]
,毕竟图片的本质就是byte[]
。因为笔者的项目中图片不是存放在数据库之中,而是需要根据查询结果动态生成条码,所以通过byte[]
导出图片。
ImageEntity是EasyPOI内置的一个JavaBean,用于设定图片的宽度和高度、导出方式、RowSpan和ColumnSpan等。调试EasyPOI的源码可知,当设置了RowSpan或者ColumnSpan之后,图片的高度设置就失效了,图片大小会自动填充图片所在的单元格。
图片导出的坑点在于导出图片的大小。假设我们将四个单元格合成为一个,希望导出的图片能填充合并之后的单元格,但是对不起,EasyPOI暂时做不到,它只会填充合并之前左上角的单元格,具体原因如下源码所示:
//BaseExportService.java ClientAnchor anchor; if (type.equals(ExcelType.HSSF)) { anchor = new HSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); } else { anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); }
可以看到,在创建图片插入位置的时候已经指定了图片的跨度为1行1列,即左上角的单元格。如果之前又设置了RowSpan或者ColumnSpan,那么图片高度的设置也会失效,最终导致导出的图片非常小。
搜索Java知音公众号,回复“后端面试”,送你一份Java面试题宝典.pdf
个人认为ImageEntity提供的RowSpan或者ColumnSpan的set方法并没有什么用,因为我们动态创建的合并单元格并不能被赋值。所以,导出图片的最好方式就是直接指定它的高度,因为宽度会自动填充单元格,模板中单元格的宽度要合适。
//ExcelExportOfTemplateUtil.java if (img.getRowspan()>1 || img.getColspan() > 1){ img.setHeight(0); PoiMergeCellUtil.addMergedRegion(cell.getSheet(),cell.getRowIndex(), cell.getRowIndex() + img.getRowspan() - 1, cell.getColumnIndex(), cell.getColumnIndex() + img.getColspan() -1); }
以上准备工作全部完成后就可以将模板和数据进行组装了,或者说是渲染,代码如下所示:
public static void exportByTemplate(String templateName, Map<String, Object> data, OutputStream fileOut) { TemplateExportParams params = new TemplateExportParams("export/template/" + templateName, true); try { Workbook workbook = ExcelExportUtil.exportExcel(params, data); workbook.write(fileOut); } catch (Exception e) { LogUtil.error("", e); } }
网上针对EasyPOI的介绍多限于最基本的行插入功能,但实际上Excel模板的需求可能各式各样。本文只是抛砖引玉,对EasyPOI中的部分概念做了详细介绍,希望帮助大家少踩坑。
如果想详细了解EasyPOI的各种功能,参考链接中的文档说明及测试项目源码就是最好的学习资料。希望大家都能得心应手地使用EasyPOI,大大提升开发效率!
EasyPOI官方文档
EasyPOI测试项目
近日有网友求助我解决EasyPOI的复杂模板配置问题,通过解决该网友的问题发现了EasyPOI中的几个坑点,补充说明几个问题。
EasyPOI에서 지원하는 복잡한 템플릿을 구성하는 방법은 복잡한 템플릿 디자인 분석 섹션에 설명되어 있습니다. 이 템플릿의 구성은 절대적으로 정확하지만 명확하게 명시되지 않은 세 가지 사항이 있으며, 조롱박을 복사할 때 누구나 실수하기 쉽습니다.
위 내용은 EasyPOI를 사용하여 Excel 템플릿 데이터(그림 포함)를 우아하게 내보냅니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!