Export dat do XLS a CzechIdM
Pro našeho klienta jsme implementovali export vybraných atributů identit do excelovského formátu XLS, jelikož otevírání CSV v tabulkovém editoru může být komplikované především z důvodu nastavení oddělovacího znaku jednotlivých sloupců. Dalším omezením je editace vzhledu výsledného exportu, s čímž si tabulkový editor pro CSV neporadí. V tomto článku si tedy ukážeme základní kroky nutné pro práci se soubory XLS pomocí frameworku Apache POI.
Apache POI
POI je javovský framework vydávaný Apache Software Foundation, takže stejně jako u ostatních produktů Apache má otevřený zdrojový kód a je vydáván pod svobodnou licencí. Současnou stabilní verzí je 3.11. Obecně se tento framework snaží poskytovat API pro standardní sadu dokumentů společnosti Microsoft, nejde tedy jen o práci se soubory typu XLS nebo XLSX, na které se v článku zaměřujeme, ale i o wordovské dokumenty nebo prezentace.
Závislosti a příprava Eclipse
Všechny třídy, které budeme pro práci s frameworkem potřebovat, jsou obsaženy v jediném JARu, nazvaném poi.jar. Tento archiv potřebuje další knihovny, ale my bude pro využití v CzechIdM potřebovat přidat pouze jedinou, a to commons-codec.jar z adresáře /lib. Ostatní, například log4j, už IdM obsahuje.
Pokud používáme k buildu projektu Ant, musíme knihovny ručně přidat do classpath. CzechIdM je distribuováno jako enterprise archiv, a jednotlivé moduly – tedy WAR a EJB – na tomto archivu závisejí. Proto pro přídání nových knihoven do classpath stačí nakopírovat knihovny do adresáře /lib projektu CzechIdM-ear. Jak WAR tak i EJB mají tento EAR v build path a můžou využívat jeho knihovny.
Pro build mavenem musíme přidat závislost do souboru pom.xml projektu CzechIdM-parent. V něm uvedeme kompletní element i s verzí, v EJB se na něj pouze odkazuje. Pro CzechIdM-parent/pom.xml:
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.10-FINAL</version> </dependency>
Pro CzechIdM-ejb/pom.xml:
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> </dependency>
Tím máme nastaveny projekty a můžeme se přesunout na samotné generování XLS.
Tvorba XLS
Samotné vytváření souboru je vlastně kopie jeho struktury, kterou ale musí programátor napsat ručně. Nejprve vytvoříme workbook a sešit, do kterých chceme zapisovat. Nastavíme font a vytvoříme styl buněk tabulky. Jednotlivé řádky sešitu vytvoříme zavoláním metody createRow(int) na aktuálním sešitu. Právě parametr metody zde vytváří poněkud nepříjemnou vlastnost, a to že pokud si nedáme pozor na číslování, můžeme si přepisovat data. Stejná vlastnost bohužel platí i pro sloupce řádků. Následující ukázka kódu zachycuje vytvoření jednoduchého souboru XLS:
public static void main(String[] args) throws Exception { FileOutputStream out = new FileOutputStream("workbook.xls"); // create a new workbook Workbook wb = new HSSFWorkbook(); // create a new sheet Sheet s = wb.createSheet(); // set the sheet name in Unicode wb.setSheetName(0, "export"); // create workbook font // set font 1 to 12 point type // make it bold // arial is the default font Font f = wb.createFont(); f.setFontHeightInPoints((short) 12); // create cell style CellStyle cs = wb.createCellStyle(); cs.setFont(f); cs.setWrapText(true); cs.setDataFormat(wb.createDataFormat().getFormat("#,##0.0")); // create row and cells Row r = s.createRow(0); for (int i = 0; i < 5; i++) { Cell c = r.createCell(i); // set cell style and its value c.setCellStyle(cs); c.setCellValue("Column no. : " + i); } // write workbook to output stream wb.write(out); out.close(); }
Nepříjemnosti s typováním
Jelikož potřebujeme generovat soubory dynamicky a formát výsledného XLS není natvrdo zadrátovaný v kódu, je potřeba zpracovávat všechny vstupní hodnoty jako Object. POI ale přijímá jako vstupní parametry pouze řetězce, datumy a čísla. Samozřejmě se opět můžeme ke všem vstupům chovat stejně a volat Object.toString(), ale to nás dostane k problémům s formátováním. Zkuste si to například pro datum nebo List, s výsledkem ale nejspíše spokojeni nebudete. Toto omezení frameworku vede na kód z následující ukázky:
protected void writeAttribute(Object value) { /* … */ // handle format of collections if (Collection.class.isAssignableFrom(value.getClass())) { Collection<?> collection = (Collection<?>) value; if (collection.isEmpty()) { cell.setCellValue(""); } else { // separate list elements by new lines StringBuilder sb = new StringBuilder(); for (Object object : collection) { sb.append(object).append("\n"); } sb.deleteCharAt(sb.length() - 1); cell.setCellValue(sb.toString()); } } /* … */ }
Pokud tedy chceme zapisovat různorodé objekty a jejich návratová hodnota metody toString() se neschoduje s požadovaným formátem XLS, musíme si pro každý formát napsat handler.
Ukončení zápisu
Zapsat výsledný workbook je velice jednoduché – třída Workbook poskyje metodu write(OutputStream), jejíž zavolání přesměruje všechna data do poskytnutého streamu. Nejsme tedy omezeni například na zápis pouze do souboru na disk, což nám v IdM přijde vhod. Zapisovací metodu „nakrmíme“ třídou ByteArrayOutputStream, z níž pohodlně dostaneme data jako pole bytů. Jakmile pak uživatel využije možnosti exportu XLS, vyskočí mu v prohlížeči nabídka na stažení sešitu a nepotřebuje nic hledat. Ukázku výsledného souboru si můžete prohlédnout na obrázku níže.
Závěr
Možnosti Apache POI jsou velmi široké a tento článek pokryl pouze jejich malou část. Pokud vytváříte aplikace, jejichž části musí být použitelné v kancelářském prostředí, je to i přes drobné nedostatky zajisté dobrá volba.
Pokud byste měli nějaké dotazy, neváhejte mě kontaktovat na info@bcvsolutions.eu.