Templat laporan Excel yang ringkas
Sesetengah templat ringkas tidak akan diterangkan secara terperinci di sini, hanya pemaparan dan kandungan konfigurasi templat akan ditunjukkan. Apabila pembaca memahami cara membuat templat yang kompleks dan mengisi nilai, yang mudah akan difahami dengan cepat.
Lihat pertama pada paparan laporan:
Kemudian lihat templat sebenar:
Setelah melihat dua gambar di atas, adakah anda sudah merasakan kuasa fungsi eksport templat?
Templat laporan Excel yang kompleks
Templat yang akan diperkenalkan di bawah agak rumit, tidak seperti situasi biasa di mana satu baris adalah satu rekod, jadi konfigurasi templat akan diperkenalkan secara terperinci, dan oleh cara Pengenalan ringkas kepada beberapa ungkapan EasyPOI.
Mari lihat rendering dahulu:
Kemudian lihat template:
Membandingkan dua gambar ini, adakah anda rasa ilmu boleh mengubah nasib anda?
Analisis reka bentuk templat kompleks
Daripada gambar templat dan paparan maklumat produk, kami mendapati keseluruhan templat sebenarnya terbahagi kepada bahagian atas dan bawah. Bahagian atas ialah maklumat pengepala yang tidak berubah, dan bahagian bawah ialah maklumat butiran produk yang dimasukkan secara kitaran. Jadi tumpuan kita adalah pada tatabahasa pada separuh masa kedua.
Lajur pertama di bahagian bawah gambar tidak ditunjukkan sepenuhnya, ia sebenarnya {{!fe: list t.id.
Perhatikan, tiada simbol }} di sini! Menurut dokumentasi rasmi EasyPOI, {{}} mewakili ungkapan dan nilai di dalamnya diperoleh berdasarkan ungkapan. Jika anda melihat dengan teliti pada gambar, anda boleh mendapati bahawa simbol penutup {{}} bagi ungkapan itu muncul di sudut kanan sebelah bawah gambar. Maksudnya, bermula dari lajur pertama {{ dan berakhir di sudut kanan bawah}}, semua yang ada di antaranya adalah sebahagian daripada ungkapan.
Oleh kerana keseluruhan maklumat templat adalah sebahagian daripada ungkapan, walaupun rentetan biasa perlu ditanda khas. Subungkapan dalam ungkapan dijelaskan satu persatu di bawah.
!fe: Melintasi data tanpa membuat baris.
Ayat dalam dokumen rasmi ini mungkin agak mengelirukan untuk difahami oleh semua orang. Apakah maksud tidak mencipta barisan? Malah, tidak mencipta baris adalah relatif kepada mencipta baris, dan ungkapan untuk mencipta baris ialah fe:.
Sama seperti setiap rekod dalam pangkalan data sepadan dengan objek entiti, mencipta baris bermakna setiap baris ialah objek entiti Atribut objek entiti ini dibalut dengan ungkapan {{}}.
Tidak membuat baris bermakna terdapat hanya satu objek objek objek dalam keseluruhan ungkapan, tetapi Objek ini adalah istimewa oleh N Entiti dalam senarai. Setiap Entiti bukan sahaja merujuk kepada model itu sendiri, tetapi juga termasuk gaya Excel, seperti bilangan sel yang didudukinya, koordinat sel, susunan susunan, dsb.
list ialah nama tersuai yang mewakili pengumpulan data dalam ungkapan Kod menggunakan senarai sebagai kunci untuk mendapatkan koleksi nilai dari Map994a833a6ffa28d85b72cb15422c29d6.
Senarai nama mudah difahami Ia hanya sebagai pemegang tempat dan boleh dipilih secara santai. Apabila EasyPOI menghuraikan senarai, ia akan mengetahui bahawa terdapat satu set nilai kunci dalam Map994a833a6ffa28d85b72cb15422c29d6 Apabila ia menghuraikan data kemudian, ia hanya boleh mengambilnya dari set.
Cari akaun rasmi Java Zhiyin, balas "Temu Bual Belakang", dan kami akan memberikan anda panduan soalan temu duga Java.pdf
t nilai pratakrif, mewakili sebarang objek dalam koleksi.
Secara kasar kita dapat merasakan dari templat bahawa setiap objek dalam senarai dipanggil t, dan t.name mewakili atribut nama t, jadi nama t boleh dipanggil secara santai, ia adalah sama dengan senarai, dan ia berfungsi sebagai pemegang tempat.
Tetapi sebenarnya ini adalah lubang besar! Jika anda menggantikan t dengan nilai lain seperti g, tulis g.name g.code, dsb. di tempat lain dalam templat, ia tidak akan dihuraikan pada akhirnya! Dokumen rasmi tidak menekankan perkara ini, tetapi penulis hanya menemuinya selepas benar-benar memijak perangkap!
]] Eksport lintasan berbilang baris watak baris baharu.
Penjelasan rasmi simbol ini juga membingungkan. Apakah watak baris baharu dan eksport? Sebenarnya, maksudnya ialah apabila ungkapan mengandungi simbol ini, kandungan selepas baris tidak akan dihuraikan, tidak kira sama ada terdapat kandungan atau gaya lain di belakangnya.
Simbol ini mesti ditulis dalam lajur terakhir setiap baris, jika tidak, bilangan baris dan lajur akan berbeza, dan pengecualian penuding nol akan dilaporkan apabila EasyPOI melaksanakan tugasan dalaman.
‘’ Tanda petikan tunggal mewakili nilai malar ‘’ Contohnya, ‘1’, maka outputnya ialah 1
Pengenalan di sini dalam dokumen rasmi juga mempunyai masalah. '' bermaksud nilai malar, tetapi sebenarnya adalah salah untuk hanya mempunyai ini dalam Excel, kerana apabila Excel menemui ' dalam sel, ia akan berfikir bahawa yang berikut adalah semua rentetan, jadi anda perlu menulis '' jenis perpustakaan: ' dalam sel. , supaya apa yang dipaparkan ialah 'Jenis perpustakaan:', bukan jenis perpustakaan rentetan:'.
Selepas analisis di atas, templat dalam gambar sebenarnya serupa dengan yang berikut:
{{!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官方文档
- https://opensource.afterturn.cn/doc/easypoi.html
EasyPOI测试项目
- https://gitee.com/lemur/easypoi-test
一些坑
近日有网友求助我解决EasyPOI的复杂模板配置问题,通过解决该网友的问题发现了EasyPOI中的几个坑点,补充说明几个问题。
Cara mengkonfigurasi templat kompleks yang disokong oleh EasyPOI telah diterangkan dalam bahagian Analisis Reka Bentuk Templat Kompleks. Konfigurasi templat ini betul-betul betul, tetapi terdapat tiga perkara yang tidak dinyatakan dengan jelas, dan mudah untuk semua orang melakukan kesilapan semasa menyalin labu:
- {{!fe: list perlu dalam senarai berasingan lajur. Dalam kod sumber EasyPOI, bilangan baris yang diperlukan untuk setiap elemen dalam senarai ditentukan berdasarkan rentang baris dan lajur sel. Sebagai contoh, dalam gambar di atas, rentang sel ialah 5 baris dan 1 lajur Dengan kata lain, setiap elemen dalam senarai akan menduduki 5 baris pada masa hadapan. Jika anda merasakan bahawa lajur ini tidak mematuhi gaya templat tersuai, anda boleh menetapkan lebar lajur lajur kepada 0, tetapi anda mesti mempunyai {{!fe: list.
- Tidak boleh ada sebarang sel kosong antara simbol permulaan dan penamat {{}} objek! Kod akan membuang pengecualian terus apabila ia menghuraikan bahawa sel itu kosong Jika anda mahu sel itu kosong, anda perlu menulis rentetan kosong: ''''.
- Watak pemecah baris]] mesti menduduki sel terakhir setiap baris! Sebagai contoh, jika terdapat 10 sel dalam baris pertama dan hanya 5 pertama digunakan dalam baris kedua, maka anda tidak boleh terus menulis aksara pemisah baris ]] pada penghujung yang ke-5 Sebaliknya, anda perlu menggabungkan 6 -10 sel dan kemudian tulis enter]]. Rujuk lajur terakhir baris tarikh pengeluaran dalam gambar di atas. Sebab untuk tetapan ini ialah EasyPOI memerlukan bilangan sel dalam setiap baris adalah sama, kerana rentang lajur setiap sel ditentukan dalam kod sumber Jika aksara pemisah baris ]] digunakan terlebih dahulu, nombor itu lajur akan berbeza daripada baris lain, kemudian Ia menjadi kucar-kacir apabila memberikan nilai, dan pengecualian indeks akan berlaku.