Spring Boot2 と Apache POI を使い、 Word(docx)のテンプレートファイル内のテキストボックス内のテキスト、パラグラフ中のテキストを置換したファイルをダウンロードするサンプル。
localhost:8080/download/word へPOSTするとWordファイルのダウンロードが始まる。
XlsMapper のような処理をやりたかったが、Wordファイル内のテンプレートキーとして入力された文字が、文字種類で分割されるなど容易に置換できそうになかったので、力技で。
参考:https://codeday.me/jp/qa/20190129/191063.html
DownloadFilesController.java
@Autowired
WordBuilder wordBuilder;
@PostMapping("/download/word")
public ModelAndView downloadWord(@RequestParam Map<String, String> requestMap) {
ModelAndView mav = new ModelAndView(wordBuilder);
return mav;
}
WordBuilder.java
package jp.co.core.kenkei.view.sample.builder;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.xwpf.usermodel.IRunBody;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.AbstractView;
import lombok.extern.log4j.Log4j2;
@Log4j2
@Component
public class WordBuilder extends AbstractView {
@Autowired
private TemplateConfiguration configuration;
private XWPFDocument createXWPFDocument() {
File wordTemplateFile = new File(configuration.getWordTemplate());
XWPFDocument workbook = null;
try (
InputStream is = new ByteArrayInputStream(
Files.readAllBytes(wordTemplateFile.toPath()));
) {
workbook = new XWPFDocument(is);
} catch (IOException | EncryptedDocumentException e) {
log.error("create workbook error", e);
}
return workbook;
}
private void outputDocx(XWPFDocument doc) {
for (XWPFParagraph p : doc.getParagraphs()) {
List<XWPFRun> runs = p.getRuns();
if (runs != null) {
for (XWPFRun r : runs) {
String text = r.getText(0);
System.out.println("--- text=" + text);
if (text != null && text.contains("あいさつ")) {
text = text.replace("あいさつ", "リプレイス-HELLO");
r.setText(text, 0);
}
}
}
}
for (XWPFTable tbl : doc.getTables()) {
for (XWPFTableRow row : tbl.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
for (XWPFParagraph p : cell.getParagraphs()) {
for (XWPFRun r : p.getRuns()) {
String text = r.getText(0);
System.out.println("TTT text=" + text);
if (text.contains("needle")) {
text = text.replace("needle", "haystack");
r.setText(text);
}
}
}
}
}
}
}
private void replaceTextBox(XWPFDocument doc, String word1, String word2) throws XmlException {
for (XWPFParagraph paragraph : doc.getParagraphs()) {
XmlCursor cursor = paragraph.getCTP().newCursor();
cursor.selectPath(
"declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//*/w:txbxContent/w:p/w:r");
List<XmlObject> ctrsintxtbx = new ArrayList<XmlObject>();
while (cursor.hasNextSelection()) {
cursor.toNextSelection();
XmlObject obj = cursor.getObject();
ctrsintxtbx.add(obj);
}
for (XmlObject obj : ctrsintxtbx) {
CTR ctr = CTR.Factory.parse(obj.xmlText());
// CTR ctr = CTR.Factory.parse(obj.newInputStream());
XWPFRun bufferrun = new XWPFRun(ctr, (IRunBody) paragraph);
String text = bufferrun.getText(0);
System.out.println("::::: " + text);
if (text != null && text.contains("文字壱") ) {
text = text.replace(text, text + word1 );
bufferrun.setText(text, 0);
obj.set(bufferrun.getCTR());
continue;
}
if (text != null && text.contains("文字弐") ) {
text = text.replace(text, text + word2 );
bufferrun.setText(text, 0);
obj.set(bufferrun.getCTR());
continue;
}
if(text == null) {
continue;
}
text = text.replace(text, "replaced:" + text );
bufferrun.setText(text, 0);
obj.set(bufferrun.getCTR());
}
}
// document.write(new FileOutputStream("WordReplaceTextInTextBoxNew.docx"));
// document.close();
}
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
final String encodedFilename = URLEncoder.encode("Wordダウンロード:サンプル.docx", "UTF-8");
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFilename);
XWPFDocument document = createXWPFDocument();
outputDocx(document);
replaceTextBox(document, request.getParameter("word1"), request.getParameter("word2") );
response.setContentType("application/msword");
document.write(response.getOutputStream());
if (document != null) {
document.close();
}
}
}