Tak jsem narazil na jeden zajímavý problém, a sice úprava DOCX dokumentu v Java aplikaci.
V podstatě dostanu dokument jako byte stream, potřebuji v něm upravit text (v tomto případě vyhodit vadné reference), a opět jej odeslat jako byte stream.
Vzhledem k tomu, že v rámci implementace často používám Apache Camel, volba padla na něj.
A tohle je tedy výsledek mého snažení:
Řešení je zapsáno ve formě JUnit testu, kdy obsah vlastního dokumentu načítám z lokálních resource. Ve vlastní aplikaci je to pochopitelně napojeno jinak.
Výsledek pak očekávám jako výstup route.
public class DocHandoverDOCXFixTest extends CamelTestSupport {
@Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
public void configure() {
/* 1 */ from("direct:start")
/* 2 */ .split(new ZipSplitter(), new ZipAggregationStrategy(true, true))
/* 3 */ .streaming()
.choice()
/* 4 */ .when(simple("${in.header.CamelFileName} regex 'word/.*\\.xml'"))
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
String msg = exchange.getIn().getBody(String.class);
/* 5 */ // tady je kód pro úpravu jednoho dokumentu
exchange.getIn().setBody(msg.getBytes(), byte[].class);
}
})
.endChoice()
.otherwise()
/* 6 */ .convertBodyTo(byte[].class)
.endChoice()
.end()
.end()
.to("mock:result");
}
};
}
@Test
public void test1() throws Exception {
/* 7 */ String sourceUrl = "classpath:cz/i/isac/resource/libreoffice/mpa01.docx";
InputStream is = new FileInputStream(ResourceUtils.getFile(sourceUrl));
/* 8 */ MockEndpoint resultEndpoint = resolveMandatoryEndpoint("mock:result", MockEndpoint.class);
resultEndpoint.expectedMessageCount(1);
/* 9 */ template.sendBodyAndHeader("direct:start", is, Exchange.FILE_NAME, "mpa01.docx");
/* 10 */resultEndpoint.assertIsSatisfied();
}
}
A ještě vysvětlení k jednotlivým krokům:
/* 1 */ - začátek definice route v Camel; pro napojení na zbytek světa používám přímé volání, ale to je závisle na kontextu použití
/* 2 */ - a tady je ta nejzajímavější část
Pro rozdělení ZIP streamu na jednotlivé soubory používám instanci ZipSplitter.
Výsledek je, že v rámci directivy split dostanu samostatné exchange pro každý soubor, který je v kontejneru zabalený. Současně je v hlavičkách zachován název souboru.
Po zpracování ale potřebuji, aby se zase vše sbalilo do ZIP streamu.
Pro to je v rámci directivy ještě použita instance ZipAggregationStrategy. Ta dělá přesně opačný postup oproti ZipSplitter. Bere jednotlivé exchange z directivy split a spojuje je do jednoho ZIP kontejneru. Ty dva parametry true říkají, že u spojených souborů má být zachována adresářová struktura a jméno souboru.
/* 3 */ - streaming znamená, že se budou soubory zpracovávat jeden po druhém, a není tedy potřeba je všechny najednou načíst do pameti
/* 4 */ - na tomhle řádku je jen ukázka toho, že na základě adresáře a jména souboru udělám nějakou akci. V tomto případě hledám všechny soubory v adresáři ./word s příponou .xml.
/* 5 */ - no a tady s tím něco udělám … ale to není předmětem tohoto povídání
/* 6 */ - jiné soubory, kterými se nezabývám, si také převedu do bytového streamu (možná to není potřeba, ale pro jistotu …)
Dále již jsou pouze kroky, které se týkají vyvolání JUnit testu:
/* 7 */ - načtení obsahu souboru z resource
/* 8 */ - nastavení mock, abych si ověřil, že dostanu zpět jeden dokument
/* 9 */ - vyvolání vlastní route
/* 10 */ – a závěrečná kontrola mock
A to je vše. Třeba vám to k něčemu bude.
pracuje na pozici IT architekta. Poslední roky se zaměřuje na integrační a komunikační projekty ve zdravotnictví. Mezi jeho koníčky patří také paragliding a jízda na horském kole.
Přečteno 33 524×
Přečteno 29 927×
Přečteno 27 156×
Přečteno 25 070×
Přečteno 20 657×